Explorar o código

feat: 好友备注,私聊设置页完善

wutiaorong hai 1 ano
pai
achega
0417eb43a6
Modificáronse 44 ficheiros con 1421 adicións e 131 borrados
  1. 7 6
      app/src/main/java/com/adealink/weparty/im/IMConfig.kt
  2. 4 0
      app/src/main/java/com/adealink/weparty/module/message/Router.kt
  3. 6 0
      app/src/main/java/com/adealink/weparty/module/profile/data/ProfileData2.kt
  4. 3 0
      app/src/main/java/com/adealink/weparty/module/profile/viewmodel/IProfileViewModel.kt
  5. 8 0
      app/src/main/res/drawable/common_f5f7fa_top_radius_12_bg.xml
  6. 10 0
      app/src/main/res/drawable/common_white_bottom_radius_12_bg.xml
  7. 1 0
      frame/imkit/build.gradle
  8. 53 3
      frame/imkit/src/main/java/com/adealink/frame/imkit/IMService.kt
  9. 2 0
      frame/imkit/src/main/java/com/adealink/frame/imkit/config/ConversationConfig.kt
  10. 2 1
      frame/imkit/src/main/java/com/adealink/frame/imkit/config/IIMConfig.kt
  11. 147 0
      frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/messagelist/provider/OfficialMessageItemProvider.kt
  12. 23 7
      frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/messagelist/provider/RichContentMessageItemProvider.kt
  13. 1 1
      frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/messagelist/provider/TextMessageItemProvider.kt
  14. 35 12
      frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/viewmodel/MessageViewModel.kt
  15. 150 83
      frame/imkit/src/main/java/com/adealink/frame/imkit/conversationlist/viewmodel/ConversationListViewModel.kt
  16. 2 2
      frame/imkit/src/main/java/com/adealink/frame/imkit/feature/refrence/ReferenceMessageItemProvider.kt
  17. 23 0
      frame/imkit/src/main/java/com/adealink/frame/imkit/model/MessageBlockRes.kt
  18. 130 0
      frame/imkit/src/main/java/com/adealink/frame/imkit/model/OfficialMessage.kt
  19. 21 0
      frame/imkit/src/main/java/com/adealink/frame/imkit/model/OfficialMessageRealContent.kt
  20. 4 2
      frame/imkit/src/main/java/com/adealink/frame/imkit/model/Tags.kt
  21. 16 0
      frame/imkit/src/main/java/com/adealink/frame/imkit/model/TextAndJumpLink.kt
  22. 1 1
      frame/imkit/src/main/java/com/adealink/frame/imkit/model/UiMessage.kt
  23. BIN=BIN
      frame/imkit/src/main/res/drawable-ldrtl-xhdpi/im_ic_bubble_left.9.png
  24. 2 2
      frame/imkit/src/main/res/layout/im_item_rich_content_message.xml
  25. 4 0
      module/message/src/main/AndroidManifest.xml
  26. 10 6
      module/message/src/main/java/com/adealink/weparty/message/MessageHomeFragment.kt
  27. 28 1
      module/message/src/main/java/com/adealink/weparty/message/conversation/ConversationFragment.kt
  28. 1 1
      module/message/src/main/java/com/adealink/weparty/message/conversation/comp/ConversationTargetInfoComp.kt
  29. 9 0
      module/message/src/main/java/com/adealink/weparty/message/conversation/provider/TargetInfoProvider.kt
  30. 274 0
      module/message/src/main/java/com/adealink/weparty/message/conversation/setting/ConversationSettingActivity.kt
  31. 150 0
      module/message/src/main/java/com/adealink/weparty/message/conversation/setting/dialog/ModifyRemarkDescDialog.kt
  32. 1 0
      module/message/src/main/java/com/adealink/weparty/message/conversationlist/ConversationListFragment.kt
  33. 10 0
      module/message/src/main/java/com/adealink/weparty/message/conversationlist/viewmodel/WeConversationSettingViewModel.kt
  34. 5 0
      module/message/src/main/res/color/message_dialog_cancel_button_color_sel.xml
  35. 5 0
      module/message/src/main/res/color/message_dialog_normal_button_color_sel.xml
  36. 112 0
      module/message/src/main/res/layout/activity_conversation_setting.xml
  37. 109 0
      module/message/src/main/res/layout/dialog_modify_remark_desc_dialog.xml
  38. 1 1
      module/message/src/main/res/layout/fragment_message_home.xml
  39. 1 1
      module/message/src/main/res/layout/layout_conversation_top_bar_center.xml
  40. 6 1
      module/message/src/main/res/layout/layout_conversation_user_info_card.xml
  41. 6 0
      module/message/src/main/res/layout/layout_message_setting_switch.xml
  42. 9 0
      module/message/src/main/res/values/strings.xml
  43. 7 0
      module/profile/src/main/java/com/adealink/weparty/profile/datasource/ProfileHttpService.kt
  44. 22 0
      module/profile/src/main/java/com/adealink/weparty/profile/viewmodel/ProfileViewModel.kt

+ 7 - 6
app/src/main/java/com/adealink/weparty/im/IMConfig.kt

@@ -6,13 +6,12 @@ import com.adealink.frame.imkit.config.IIMConfig
 import com.adealink.frame.imkit.userinfo.UserDataProvider
 import com.adealink.frame.imkit.userinfo.model.IMUserInfo
 import com.adealink.frame.log.ILog
-import com.adealink.frame.router.Router
 import com.adealink.frame.util.AppUtil
 import com.adealink.weparty.App
 import com.adealink.weparty.commonui.ext.onSuccess
 import com.adealink.weparty.module.profile.ProfileModule
 import com.adealink.weparty.module.profile.view.UserNameTextView.Companion.getDisplayName
-import com.adealink.weparty.module.webview.Web
+import com.adealink.weparty.util.goLocalLinkPage
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.launch
 
@@ -57,11 +56,13 @@ class IMConfig: IIMConfig {
             }
         }
 
-    override fun routeToWebActivity(url: String) {
+    override fun getAppLanguageCode(): String {
+        return App.instance.languageManager.getLanguageCode()
+    }
+
+    override fun routeUrl(url: String) {
         val currentActivity = AppUtil.currentActivity ?: return
-        Router.build(currentActivity, Web.FullScreen.PATH)
-            .putExtra(Web.Common.EXTRA_URL, url)
-            .start()
+        goLocalLinkPage(currentActivity, url)
     }
 
 }

+ 4 - 0
app/src/main/java/com/adealink/weparty/module/message/Router.kt

@@ -9,6 +9,8 @@ interface Message {
             const val EXTRA_CONVERSATION_TYPE = "extra_conversation_type"
             const val EXTRA_CONVERSATION_IDENTIFIER = "extra_conversation_identifier"
             const val EXTRA_CONVERSATION_PAGE_TYPE = "extra_conversation_page_type"
+            const val EXTRA_REMARK_DESC = "extra_remark_desc"
+            const val EXTRA_TYPE = "extra_type"
         }
     }
 
@@ -50,6 +52,8 @@ interface Message {
             const val PATH = "/conversation"
             const val LIST = "/conversation_list"
             const val FRAGMENT = "${PATH}/fragment"
+            const val SETTING = "${PATH}/setting"
+            const val MODIFY_REMARK_DESC = "${PATH}/modify_remark_desc"
         }
     }
 

+ 6 - 0
app/src/main/java/com/adealink/weparty/module/profile/data/ProfileData2.kt

@@ -852,4 +852,10 @@ data class AddRemarkAndDescReq(
     @SerializedName("desc") val desc: String? = null,
     @SerializedName("remark") val remark: String? = null,
     @SerializedName("uid") val peerUid: Long
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class RemarkAndDescRes(
+    @SerializedName("desc") val desc: String,
+    @SerializedName("remark") val remark: String
 )

+ 3 - 0
app/src/main/java/com/adealink/weparty/module/profile/viewmodel/IProfileViewModel.kt

@@ -7,6 +7,7 @@ import com.adealink.weparty.module.profile.data.GifAvatarHadReview
 import com.adealink.weparty.module.profile.data.GifAvatarLastLevel
 import com.adealink.weparty.module.profile.data.GifFreeTimeType
 import com.adealink.weparty.module.profile.data.RegionUserRes
+import com.adealink.weparty.module.profile.data.RemarkAndDescRes
 import com.adealink.weparty.module.profile.data.UserConfigType
 import com.adealink.weparty.module.profile.data.UserInfo
 import com.adealink.weparty.module.profile.data.UserInfo.Companion.NECESSARY_USER_ATTR_SET
@@ -73,4 +74,6 @@ interface IProfileViewModel {
     fun updateUserNoteName(uid: Long, note: String): LiveData<Rlt<Any>>
 
     fun updateUserDesc(uid: Long, desc: String): LiveData<Rlt<Any>>
+
+    fun getUserRemarkAndDesc(uid: Long): LiveData<Rlt<RemarkAndDescRes>>
 }

+ 8 - 0
app/src/main/res/drawable/common_f5f7fa_top_radius_12_bg.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/color_FFF5F7FA" />
+    <corners
+        android:topLeftRadius="12dp"
+        android:topRightRadius="12dp" />
+</shape>

+ 10 - 0
app/src/main/res/drawable/common_white_bottom_radius_12_bg.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+    android:shape="rectangle">
+    <solid android:color="@color/white" />
+    <corners
+        android:bottomLeftRadius="12dp"
+        android:bottomRightRadius="12dp"
+        android:topLeftRadius="0dp"
+        android:topRightRadius="0dp" />
+</shape>

+ 1 - 0
frame/imkit/build.gradle

@@ -64,6 +64,7 @@ dependencies {
     compileOnly libs.frame.mvvm
     compileOnly libs.frame.aab
     compileOnly libs.frame.zero
+    compileOnly libs.frame.data
     implementation libs.smart.refresh.layout.kernel
     implementation libs.smart.refresh.header.material
     implementation libs.smart.refresh.footer.classics

+ 53 - 3
frame/imkit/src/main/java/com/adealink/frame/imkit/IMService.kt

@@ -12,6 +12,8 @@ import com.adealink.frame.imkit.event.actionevent.RefreshEvent
 import com.adealink.frame.imkit.event.actionevent.SendEvent
 import com.adealink.frame.imkit.event.actionevent.SendMediaEvent
 import com.adealink.frame.imkit.manager.UnReadMessageManager
+import com.adealink.frame.imkit.model.OfficialMessage
+import com.adealink.frame.imkit.model.TAG_IM_MSG_BLOCK
 import com.adealink.frame.imkit.model.TAG_IM_SERVICE
 import com.adealink.frame.imkit.resend.ResendManager
 import com.adealink.frame.imkit.userinfo.IMUserInfoManager
@@ -75,6 +77,7 @@ class IMService(val config: IIMConfig) {
     private val readReceiptObserverList = CopyOnWriteArrayList<RongIMClient.ReadReceiptListener>()
     private val syncConversationReadStatusListeners = CopyOnWriteArrayList<RongIMClient.SyncConversationReadStatusListener>()
     private val cancelSendMediaMessageListeners = CopyOnWriteArrayList<RongIMClient.ResultCallback<Message>>()
+    private val messageBlockListeners = CopyOnWriteArrayList<IRongCoreListener.MessageBlockListener>()
 
     /** 连接状态变化的监听器。  */
     private val connectionStatusListener =
@@ -203,6 +206,17 @@ class IMService(val config: IIMConfig) {
             }
         }
 
+    /**
+     * 敏感信息拦截监听器
+     */
+    private val messageBlockListener: IRongCoreListener.MessageBlockListener =
+        IRongCoreListener.MessageBlockListener { info ->
+            LogUtil.e(TAG_IM_MSG_BLOCK, "$info")
+            for (item in messageBlockListeners) {
+                item.onMessageBlock(info)
+            }
+        }
+
     /**
      * 初始化 SDK,在整个应用程序全局,只需要调用一次。
      *
@@ -244,11 +258,10 @@ class IMService(val config: IIMConfig) {
         RongIMClient.getInstance().setConversationStatusListener(conversationStatusListener)
         RongIMClient.setReadReceiptListener(readReceiptListener)
         RongIMClient.getInstance().setSyncConversationReadStatusListener(syncConversationReadStatusListener)
+        RongIMClient.getInstance().setMessageBlockListener(messageBlockListener)
         IMUserInfoManager.setUserInfoProvider(config.userInfoProvider)
     }
 
-
-
     /**
      * 连接服务器,在整个应用程序全局,只需要调用一次。
      *
@@ -269,7 +282,7 @@ class IMService(val config: IIMConfig) {
         token: String?, timeLimit: Int, connectCallback: RongIMClient.ConnectCallback? = null
     ) {
         //建立连接前,注册自定义消息,否则sdk无法识别
-        RongIMClient.registerMessageType(listOf(SightMessage::class.java))
+        RongIMClient.registerMessageType(listOf(SightMessage::class.java, OfficialMessage::class.java))
         val connectOption = ConnectOption.obtain(token, timeLimit)
         connect(connectOption, connectCallback)
     }
@@ -474,6 +487,22 @@ class IMService(val config: IIMConfig) {
         syncConversationReadStatusListeners.remove(listener)
     }
 
+    fun addMessageBlockListener(
+        listener: IRongCoreListener.MessageBlockListener
+    ) {
+        if (messageBlockListeners.contains(listener)) {
+            return
+        }
+        messageBlockListeners.add(listener)
+    }
+
+    fun removeMessageBlockListener(
+        listener: IRongCoreListener.MessageBlockListener
+    ) {
+        messageBlockListeners.remove(listener)
+    }
+
+
     fun addConversationEventListener(listener: ConversationEventListener) {
         if (!conversationEventListener.contains(listener)) {
             conversationEventListener.add(listener)
@@ -1231,6 +1260,17 @@ class IMService(val config: IIMConfig) {
         }
     }
 
+    /**
+     * 获取指定会话。
+     */
+    fun getConversation(
+        conversationType: Conversation.ConversationType,
+        targetId: String,
+        callback: RongIMClient.ResultCallback<Conversation?>?
+    ) {
+        RongIMClient.getInstance().getConversation(conversationType, targetId, callback)
+    }
+
     /**
      * 获取指定会话历史消息。
      *
@@ -1521,6 +1561,16 @@ class IMService(val config: IIMConfig) {
                 })
     }
 
+    /**
+     * 查询某一会话为置顶状态
+     */
+    fun isConversationToTop(
+        conversationIdentifier: ConversationIdentifier,
+        callback: IRongCoreCallback.ResultCallback<Boolean>?
+    ) {
+        RongCoreClient.getInstance().getConversationTopStatus(conversationIdentifier.targetId, conversationIdentifier.type, callback)
+    }
+
     /**
      * 设置某一会话为置顶或者取消置顶,回调方式获取设置是否成功。
      *

+ 2 - 0
frame/imkit/src/main/java/com/adealink/frame/imkit/config/ConversationConfig.kt

@@ -8,6 +8,7 @@ import com.adealink.frame.imkit.conversation.messagelist.provider.IConversationS
 import com.adealink.frame.imkit.conversation.messagelist.provider.IMessageProvider
 import com.adealink.frame.imkit.conversation.messagelist.provider.ImageMessageItemProvider
 import com.adealink.frame.imkit.conversation.messagelist.provider.InformationNotificationMessageItemProvider
+import com.adealink.frame.imkit.conversation.messagelist.provider.OfficialMessageItemProvider
 import com.adealink.frame.imkit.conversation.messagelist.provider.RecallNotificationMessageItemProvider
 import com.adealink.frame.imkit.conversation.messagelist.provider.RichContentMessageItemProvider
 import com.adealink.frame.imkit.conversation.messagelist.provider.SightMessageItemProvider
@@ -75,6 +76,7 @@ class ConversationConfig {
         addMessageProvider(RecallNotificationMessageItemProvider())
         addMessageProvider(UnknownMessageItemProvider())
         addMessageProvider(InformationNotificationMessageItemProvider())
+        addMessageProvider(OfficialMessageItemProvider())
         supportReadReceiptConversationTypes.add(Conversation.ConversationType.PRIVATE)
     }
 

+ 2 - 1
frame/imkit/src/main/java/com/adealink/frame/imkit/config/IIMConfig.kt

@@ -9,5 +9,6 @@ interface IIMConfig {
     val logger: ILog
     val userInfoProvider: UserDataProvider.UserInfoProvider
 
-    fun routeToWebActivity(url: String)
+    fun getAppLanguageCode(): String
+    fun routeUrl(url: String)
 }

+ 147 - 0
frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/messagelist/provider/OfficialMessageItemProvider.kt

@@ -0,0 +1,147 @@
+package com.adealink.frame.imkit.conversation.messagelist.provider
+
+import android.text.Spannable
+import android.text.SpannableString
+import android.text.SpannableStringBuilder
+import android.text.Spanned
+import android.text.method.LinkMovementMethod
+import android.text.style.ClickableSpan
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import com.adealink.frame.data.json.froJsonErrorNull
+import com.adealink.frame.image.view.NetworkImageView
+import com.adealink.frame.imkit.IMService
+import com.adealink.frame.imkit.R
+import com.adealink.frame.imkit.model.OfficialMessage
+import com.adealink.frame.imkit.model.OfficialMessageRealContent
+import com.adealink.frame.imkit.model.UiMessage
+import com.adealink.frame.imkit.widget.adapter.IViewProviderListener
+import com.adealink.frame.imkit.widget.adapter.ViewHolder
+import com.adealink.frame.util.DisplayUtil
+import com.facebook.drawee.generic.RoundingParams
+import io.rong.imlib.model.MessageContent
+
+class OfficialMessageItemProvider : BaseMessageItemProvider<OfficialMessage>() {
+    init {
+        config.showReadState = true
+        config.showContentBubble = true
+    }
+
+    override fun onCreateMessageContentViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
+        val view: View =
+            LayoutInflater.from(parent.context)
+                .inflate(R.layout.im_item_rich_content_message, parent, false)
+        return ViewHolder(view.context, view)
+    }
+
+    override fun bindMessageContentViewHolder(
+        holder: ViewHolder,
+        parentHolder: ViewHolder,
+        officialMessage: OfficialMessage,
+        uiMessage: UiMessage,
+        position: Int,
+        list: List<UiMessage>,
+        listener: IViewProviderListener<UiMessage>
+    ) {
+        val lan2OfficialMessage = froJsonErrorNull<Map<String, OfficialMessageRealContent>>(officialMessage.extra)
+        val languageCode = IMService.innerService.config.getAppLanguageCode()
+        val curLanOfficialMessage = lan2OfficialMessage?.get(languageCode)
+        if (curLanOfficialMessage == null) {
+            //没有当前语言的文案,直接展示默认的
+            setContentText(holder, OfficialMessageRealContent(
+                officialMessage.title ?: "",
+                officialMessage.content?: "",
+                officialMessage.bannerUrl?: "",
+                officialMessage.bannerUrlJumpLink?: "",
+                officialMessage.jumpLink?: "",
+                officialMessage.jumpLinkText?: ""
+            ))
+            return
+        }
+        setContentText(holder, curLanOfficialMessage)
+    }
+
+    private fun setContentText(holder: ViewHolder, officialMessageContent: OfficialMessageRealContent?) {
+        officialMessageContent ?: return
+        val img = holder.getView<NetworkImageView>(R.id.im_img)
+        val contentTv = holder.getView<TextView>(R.id.im_content)
+        val titleTv = holder.getView<TextView>(R.id.im_title)
+        if (img == null || contentTv == null || titleTv == null) {
+            return
+        }
+        if (officialMessageContent.bannerUrl.isEmpty()) {
+            img.visibility = View.GONE
+        } else {
+            if (DisplayUtil.isRtlLayout()) {
+                val roundingParams = RoundingParams.fromCornersRadii(DisplayUtil.dp2px(12f).toFloat(), 0f, 0f, 0f)
+                img.hierarchy.roundingParams = roundingParams
+            } else {
+                val roundingParams = RoundingParams.fromCornersRadii(0f, DisplayUtil.dp2px(12f).toFloat(), 0f, 0f)
+                img.hierarchy.roundingParams = roundingParams
+            }
+            img.visibility = View.VISIBLE
+            img.setImageUrl(officialMessageContent.bannerUrl)
+        }
+        //展示标题
+        titleTv.text = officialMessageContent.title
+        val jumpLink = officialMessageContent.jumpLink
+        val jumpText = officialMessageContent.jumpLinkText
+        val content = officialMessageContent.content
+        //判断内容是否要拼接跳转链接
+        if (jumpText.isEmpty()) {
+            //没有拼接文案,直接展示文案
+            contentTv.text = officialMessageContent.content
+            return
+        }
+        // 拼接文本 content + jumpText
+        val spannable = SpannableStringBuilder(content)
+        val start = spannable.length
+        spannable.append(" ")
+        spannable.append(jumpText)
+        //jumpText点击事件
+        spannable.setSpan(object : ClickableSpan() {
+            override fun onClick(widget: View) {
+                IMService.innerService.config.routeUrl(jumpLink)
+            }
+        }, start, spannable.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
+        contentTv.text = spannable
+        contentTv.movementMethod = LinkMovementMethod.getInstance()
+    }
+
+    override fun onItemClick(
+        holder: ViewHolder,
+        officialMessage: OfficialMessage,
+        uiMessage: UiMessage,
+        position: Int,
+        list: List<UiMessage>,
+        listener: IViewProviderListener<UiMessage>
+    ): Boolean {
+        val bannerJumpUrl = officialMessage.bannerUrlJumpLink
+        if (!bannerJumpUrl.isNullOrEmpty()) {
+            IMService.innerService.config.routeUrl(bannerJumpUrl)
+            return true
+        }
+        return false
+    }
+
+    override fun isMessageViewType(messageContent: MessageContent): Boolean {
+        return messageContent is OfficialMessage
+    }
+
+    override fun getSummarySpannable(
+        officialMessage: OfficialMessage
+    ): Spannable {
+        var content = officialMessage.content
+        if (!content.isNullOrEmpty()) {
+            content = content.replace("\n", " ")
+            if (content.length > 100) {
+                content = content.substring(0, 100)
+            }
+            return SpannableString(content)
+        } else {
+            return SpannableString("")
+        }
+    }
+}

+ 23 - 7
frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/messagelist/provider/RichContentMessageItemProvider.kt

@@ -6,19 +6,23 @@ import android.text.TextUtils
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
+import android.widget.TextView
 import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.image.view.NetworkImageView
 import com.adealink.frame.imkit.IMService
+import com.adealink.frame.imkit.R
 import com.adealink.frame.imkit.model.UiMessage
 import com.adealink.frame.imkit.widget.adapter.IViewProviderListener
 import com.adealink.frame.imkit.widget.adapter.ViewHolder
-import com.adealink.frame.imkit.R
+import com.adealink.frame.util.DisplayUtil
+import com.facebook.drawee.generic.RoundingParams
 import io.rong.imlib.model.MessageContent
 import io.rong.message.RichContentMessage
 
 class RichContentMessageItemProvider : BaseMessageItemProvider<RichContentMessage>() {
     init {
         config.showReadState = true
+        config.showContentBubble = true
     }
 
     override fun onCreateMessageContentViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@@ -38,14 +42,26 @@ class RichContentMessageItemProvider : BaseMessageItemProvider<RichContentMessag
         listener: IViewProviderListener<UiMessage>
     ) {
         val img = holder.getView<NetworkImageView>(R.id.im_img)
+        val contentTv = holder.getView<TextView>(R.id.im_content)
+        val titleTv = holder.getView<TextView>(R.id.im_title)
+        if (img == null || contentTv == null || titleTv == null) {
+            return
+        }
+        if (DisplayUtil.isRtlLayout()) {
+            val roundingParams = RoundingParams.fromCornersRadii(DisplayUtil.dp2px(12f).toFloat(), 0f, 0f, 0f)
+            img.hierarchy.roundingParams = roundingParams
+        } else {
+            val roundingParams = RoundingParams.fromCornersRadii(0f, DisplayUtil.dp2px(12f).toFloat(), 0f, 0f)
+            img.hierarchy.roundingParams = roundingParams
+        }
         if (richContentMessage.imgUrl.isNullOrEmpty()) {
-            img?.visibility = View.GONE
+            img.visibility = View.GONE
         } else {
-            img?.visibility = View.VISIBLE
-            img?.setImageUrl(richContentMessage.imgUrl)
+            img.visibility = View.VISIBLE
+            img.setImageUrl(richContentMessage.imgUrl)
         }
-        holder.setText(R.id.im_title, richContentMessage.title)
-        holder.setText(R.id.im_content, richContentMessage.content)
+        contentTv.text = richContentMessage.content
+        titleTv.text = richContentMessage.title
     }
 
     override fun onItemClick(
@@ -56,7 +72,7 @@ class RichContentMessageItemProvider : BaseMessageItemProvider<RichContentMessag
         list: List<UiMessage>,
         listener: IViewProviderListener<UiMessage>
     ): Boolean {
-        IMService.innerService.config.routeToWebActivity(richContentMessage.url)
+        IMService.innerService.config.routeUrl(richContentMessage.url)
         return true
     }
 

+ 1 - 1
frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/messagelist/provider/TextMessageItemProvider.kt

@@ -79,7 +79,7 @@ class TextMessageItemProvider : BaseMessageItemProvider<TextMessage>() {
             var result = false
             val str = link.lowercase(Locale.getDefault())
             if (str.startsWith("http") || str.startsWith("https")) {
-                IMService.innerService.config.routeToWebActivity(link)
+                IMService.innerService.config.routeUrl(link)
                 result = true
             }
             result

+ 35 - 12
frame/imkit/src/main/java/com/adealink/frame/imkit/conversation/viewmodel/MessageViewModel.kt

@@ -9,6 +9,7 @@ import androidx.lifecycle.MediatorLiveData
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.data.json.froJsonErrorNull
 import com.adealink.frame.imkit.BaseConversationEventListener
 import com.adealink.frame.imkit.IMService
 import com.adealink.frame.imkit.MessageItemLongClickAction
@@ -36,8 +37,9 @@ import com.adealink.frame.imkit.event.uievent.ToastEvent
 import com.adealink.frame.imkit.listener.IIMUserInfoListener
 import com.adealink.frame.imkit.manager.AudioPlayManager
 import com.adealink.frame.imkit.manager.IAudioPlayListener
+import com.adealink.frame.imkit.model.MessageBlockRlt
 import com.adealink.frame.imkit.model.State
-import com.adealink.frame.imkit.model.TAG_IM_MESSAGE_VM
+import com.adealink.frame.imkit.model.TAG_IM_MSG_VM
 import com.adealink.frame.imkit.model.UiMessage
 import com.adealink.frame.imkit.userinfo.IMUserInfoManager
 import com.adealink.frame.imkit.userinfo.model.IMUserInfo
@@ -49,8 +51,10 @@ import com.adealink.frame.util.AppUtil
 import io.rong.common.FileUtils
 import io.rong.imlib.IRongCallback
 import io.rong.imlib.IRongCoreEnum
+import io.rong.imlib.IRongCoreListener
 import io.rong.imlib.MessageTag
 import io.rong.imlib.RongIMClient
+import io.rong.imlib.model.BlockedMessageInfo
 import io.rong.imlib.model.Conversation
 import io.rong.imlib.model.ConversationIdentifier
 import io.rong.imlib.model.Message
@@ -218,7 +222,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
                                             //TODO wtr高清语音自动下载
                                         } else {
                                             LogUtil.e(
-                                                TAG_IM_MESSAGE_VM,
+                                                TAG_IM_MSG_VM,
                                                 "enableAutomaticDownloadVoiceMsg disable"
                                             )
                                         }
@@ -256,7 +260,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
             if (!isSameConversationMessage(message)) {
                 return@OnRecallMessageListener false
             }
-            LogUtil.d(TAG_IM_MESSAGE_VM, "onRecallMessage")
+            LogUtil.d(TAG_IM_MSG_VM, "onRecallMessage")
             val uiMessage: UiMessage? = findUIMessage(message.messageId)
             // 当uiMessage = null, 从_newUnReadMessages中遍历
             val newUnreadMessage: UiMessage? = findNewUnreadMessage(message.messageId)
@@ -311,6 +315,22 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
             }
         }
 
+    private val messageBlockListener: IRongCoreListener.MessageBlockListener =
+        object : IRongCoreListener.MessageBlockListener {
+            override fun onMessageBlock(info: BlockedMessageInfo?) {
+                info ?: return
+                val messageBlockRlt = froJsonErrorNull<MessageBlockRlt>(info.extra) ?: return
+                val messageBlockRes = messageBlockRlt.result ?: return
+                val code = messageBlockRes.code
+                val message = messageBlockRes.message
+                if (code != 200) {
+                    _pageEventLiveData.send(
+                        ToastEvent(message.ifEmpty { code.toString() })
+                    )
+                }
+            }
+        }
+
     init {
         IMService.innerService.addAsyncOnReceiveMessageListener(onReceiveMessageListener)
         IMService.innerService.addConnectionStatusListener(connectionStatusListener)
@@ -318,6 +338,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
         IMService.innerService.addOnRecallMessageListener(recallMessageListener)
         IMService.innerService.addMessageEventListener(this)
         IMService.innerService.addConversationEventListener(conversationEventListener)
+        IMService.innerService.addMessageBlockListener(messageBlockListener)
         IMUserInfoManager.addListener(this)
     }
 
@@ -332,7 +353,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
 
     /** 初始化加载本地消息 下拉加载历史消息  */
     fun onGetHistoryMessage(messages: List<Message>, loadCnt: Int? = null) {
-        LogUtil.d(TAG_IM_MESSAGE_VM, "onGetHistoryMessage, ${messages.map { it.messageId }}, loadCnt:${loadCnt}")
+        LogUtil.d(TAG_IM_MSG_VM, "onGetHistoryMessage, ${messages.map { it.messageId }}, loadCnt:${loadCnt}")
 
         //待插入的头部消息列表
         val toInsertTopMessageList = mutableListOf<Message>()
@@ -591,7 +612,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
             }
             // 如果被 voip 占用通道,则不播放,弹提示框
             if (AudioPlayManager.getInstance().isInVOIPMode(AppUtil.appContext)) {
-                _pageEventLiveData.setValue(
+                _pageEventLiveData.send(
                     ToastEvent(getCompatString(R.string.rc_voip_occupying))
                 )
                 return
@@ -608,7 +629,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
             }
             // 如果被 voip 占用通道,则不播放,弹提示框
             if (AudioPlayManager.getInstance().isInVOIPMode(AppUtil.appContext)) {
-                _pageEventLiveData.setValue(
+                _pageEventLiveData.send(
                     ToastEvent(getCompatString(R.string.rc_voip_occupying))
                 )
                 return
@@ -712,12 +733,12 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
 
     private fun findNextHQVoice(uiMessage: UiMessage) {
         if (!IMConfigCenter.conversationConfig.playAudioContinuous) {
-            LogUtil.e(TAG_IM_MESSAGE_VM, "playAudioContinuous is disabled.")
+            LogUtil.e(TAG_IM_MSG_VM, "playAudioContinuous is disabled.")
             return
         }
         val position = findPositionByMessageId(uiMessage.getMessage().messageId)
         if (position == -1) {
-            LogUtil.d(TAG_IM_MESSAGE_VM, "the message isn't found in the list.")
+            LogUtil.d(TAG_IM_MSG_VM, "the message isn't found in the list.")
             return
         }
         for (i in position until _uiMessages.size) {
@@ -982,7 +1003,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
     }
 
     override fun onClearMessages(event: ClearEvent) {
-        LogUtil.d(TAG_IM_MESSAGE_VM, "onClearMessages")
+        LogUtil.d(TAG_IM_MSG_VM, "onClearMessages")
         if (event.targetId == curTargetId
             && event.conversationType == curConversationType
         ) {
@@ -1142,7 +1163,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
 
                     override fun onError(errorCode: RongIMClient.ErrorCode?) {
                         LogUtil.e(
-                            TAG_IM_MESSAGE_VM,
+                            TAG_IM_MSG_VM,
                             "sendReadReceiptRequest failed, errorCode = $errorCode"
                         )
                         if ((errorCode != null
@@ -1238,6 +1259,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
         IMService.innerService.removeOnRecallMessageListener(recallMessageListener)
         IMService.innerService.removeMessageEventListener(this)
         IMService.innerService.removeConversationEventListener(conversationEventListener)
+        IMService.innerService.removeMessageBlockListener(messageBlockListener)
         IMUserInfoManager.removeListener(this)
     }
 
@@ -1257,7 +1279,7 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
                 if (selected) {
                     _selectedUiMessage.remove(data)
                     if (_selectedUiMessage.size <= 0) {
-                        _pageEventLiveData.postValue(
+                        _pageEventLiveData.send(
                             InputBarEvent(InputBarEvent.Type.InactiveMoreMenu, null)
                         )
                     }
@@ -1267,8 +1289,9 @@ open class MessageViewModel : BaseViewModel(), MessageEventListener, IIMUserInfo
                     if (_selectedUiMessage.size < IMConfigCenter.conversationConfig.maxMessageSelectedCount) {
                         _selectedUiMessage.add(data)
                         if (_selectedUiMessage.size > 0 && preSize <= 0) {
-                            _pageEventLiveData.value =
+                            _pageEventLiveData.send(
                                 InputBarEvent(InputBarEvent.Type.ActiveMoreMenu, null)
+                            )
                         }
                         data.isSelected = true
                         refreshSingleMessage(data)

+ 150 - 83
frame/imkit/src/main/java/com/adealink/frame/imkit/conversationlist/viewmodel/ConversationListViewModel.kt

@@ -48,7 +48,8 @@ import kotlinx.coroutines.launch
 import java.lang.ref.WeakReference
 import java.util.concurrent.CopyOnWriteArrayList
 
-open class ConversationListViewModel : BaseViewModel(),
+open class ConversationListViewModel(private val isConversationListScene: Boolean = true) :
+    BaseViewModel(),
     IIMUserInfoListener, CoroutineScope {
     val serialHandler = Dispatcher.getSerialHandler()
     override val coroutineContext = SupervisorJob() + serialHandler.asCoroutineDispatcher()
@@ -60,6 +61,7 @@ open class ConversationListViewModel : BaseViewModel(),
         IMConfigCenter.conversationListConfig.getDataProcessor().supportedTypes()
     private var sizePerPage: Int
     private var pageLimit: Int
+
     /** 是否优先显示置顶会话(查询结果的排序方式,是否置顶优先,true 表示置顶会话优先返回,false 结果只以会话时间排序)  */
     private var isTopPriority: Boolean
     private var uiConversationList = CopyOnWriteArrayList<BaseUiConversation>()
@@ -333,17 +335,21 @@ open class ConversationListViewModel : BaseViewModel(),
         isTopPriority = IMConfigCenter.conversationListConfig.topPriority
 
         _conversationListLiveData = MediatorLiveData<List<BaseUiConversation>>()
-        IMUserInfoManager.addListener(this)
-        IMService.innerService.addAsyncOnReceiveMessageListener(onReceiveMessageListener)
-        IMService.innerService.addConnectionStatusListener(connectionStatusListener)
-        IMService.innerService.addConversationStatusListener(conversationStatusListener)
-        IMService.innerService.addReadReceiptListener(readReceiptListener)
-        IMService.innerService.addSyncConversationReadStatusListener(syncConversationReadStatusListener)
-        IMService.innerService.addOnRecallMessageListener(onRecallMessageListener)
-        IMService.innerService.addConversationEventListener(conversationEventListener)
-        IMService.innerService.addMessageEventListener(messageEventListener)
-        IMService.innerService.addCancelSendMediaMessageListener(cancelSendMediaMessageListener)
-        updateNoticeContent(IMService.innerService.getCurrentConnectionStatus())
+        if (isConversationListScene) {
+            IMUserInfoManager.addListener(this)
+            IMService.innerService.addAsyncOnReceiveMessageListener(onReceiveMessageListener)
+            IMService.innerService.addConnectionStatusListener(connectionStatusListener)
+            IMService.innerService.addConversationStatusListener(conversationStatusListener)
+            IMService.innerService.addReadReceiptListener(readReceiptListener)
+            IMService.innerService.addSyncConversationReadStatusListener(
+                syncConversationReadStatusListener
+            )
+            IMService.innerService.addOnRecallMessageListener(onRecallMessageListener)
+            IMService.innerService.addConversationEventListener(conversationEventListener)
+            IMService.innerService.addMessageEventListener(messageEventListener)
+            IMService.innerService.addCancelSendMediaMessageListener(cancelSendMediaMessageListener)
+            updateNoticeContent(IMService.innerService.getCurrentConnectionStatus())
+        }
     }
 
     /**
@@ -420,14 +426,20 @@ open class ConversationListViewModel : BaseViewModel(),
                 refreshConversationList()
                 return@post
             }
-            LogUtil.d(TAG_IM_CONVERSATION_VM, "getConversationListByPage. size:" + conversations.size)
+            LogUtil.d(
+                TAG_IM_CONVERSATION_VM,
+                "getConversationListByPage. size:" + conversations.size
+            )
             val copyList =
                 CopyOnWriteArrayList(conversations)
             val filterResult: List<Conversation> = dataFilter.filtered(copyList)
             if (filterResult.isNotEmpty()) {
                 for (conversation in filterResult) {
                     val oldItem: BaseUiConversation? =
-                        findConversationFromList(conversation.conversationType, conversation.targetId)
+                        findConversationFromList(
+                            conversation.conversationType,
+                            conversation.targetId
+                        )
                     if (oldItem != null) {
                         oldItem.onConversationUpdate(conversation)
                     } else {
@@ -457,7 +469,10 @@ open class ConversationListViewModel : BaseViewModel(),
         }
     }
 
-    protected fun findConversationFromList(conversationType: Conversation.ConversationType, targetId: String): BaseUiConversation? {
+    protected fun findConversationFromList(
+        conversationType: Conversation.ConversationType,
+        targetId: String
+    ): BaseUiConversation? {
         val baseUiConversationArrayList: List<BaseUiConversation> =
             ArrayList<BaseUiConversation>(this.uiConversationList)
         for (i in baseUiConversationArrayList.indices.reversed()) {
@@ -487,11 +502,11 @@ open class ConversationListViewModel : BaseViewModel(),
     private fun getDeletedMsgConversation(
         type: Conversation.ConversationType, targetId: String, deleteMsgId: IntArray
     ) {
-        RongIMClient.getInstance()
+        IMService.innerService
             .getConversation(
                 type,
                 targetId,
-                object : RongIMClient.ResultCallback<Conversation>() {
+                object : RongIMClient.ResultCallback<Conversation?>() {
                     override fun onSuccess(conversation: Conversation?) {
                         if (conversation == null) {
                             return
@@ -508,7 +523,10 @@ open class ConversationListViewModel : BaseViewModel(),
                             }
                         }
                         serialHandler.post {
-                            if (conversation.sentStatus == Message.SentStatus.FAILED && ResendManager.needResend(conversation.latestMessageId)) {
+                            if (conversation.sentStatus == Message.SentStatus.FAILED && ResendManager.needResend(
+                                    conversation.latestMessageId
+                                )
+                            ) {
                                 conversation.sentStatus = Message.SentStatus.SENDING
                             }
                             updateByConversation(conversation)
@@ -556,6 +574,39 @@ open class ConversationListViewModel : BaseViewModel(),
         return false
     }
 
+    fun isConversationToTop(cId: ConversationIdentifier): LiveData<Boolean> {
+        val liveData = MutableLiveData<Boolean>()
+        viewModelScope.launch {
+            IMService.innerService
+                .isConversationToTop(
+                    cId,
+                    object : IRongCoreCallback.ResultCallback<Boolean>() {
+                        override fun onSuccess(t: Boolean?) {
+                            liveData.send(t == true)
+                        }
+
+                        override fun onError(e: IRongCoreEnum.CoreErrorCode?) {
+                            liveData.send(false)
+                        }
+
+                    }
+                )
+        }
+        return liveData
+    }
+
+    fun setConversationToTop(
+        cId: ConversationIdentifier,
+        isTop: Boolean
+    ) {
+        IMService.innerService
+            .setConversationToTop(
+                cId,
+                isTop,
+                null
+            )
+    }
+
     fun onViewClick(clickType: Int, data: BaseUiConversation) {
         when (clickType) {
             ConversationClickType.READ_CLICK -> {
@@ -570,43 +621,67 @@ open class ConversationListViewModel : BaseViewModel(),
         }
     }
 
-    fun onPinClick(data: BaseUiConversation) {
+    fun deleteConversation(data: BaseUiConversation) {
         val cId = data.conversationIdentifier ?: return
+        IMService.innerService.setConversationToTop(cId, false, null)
+        IMService.innerService.removeConversation(cId.type, cId.targetId, null)
         IMService.innerService
-            .setConversationToTop(
-                cId,
-                !data.conversation.isTop,
-                object : RongIMClient.ResultCallback<Boolean>() {
-                    override fun onSuccess(aBoolean: Boolean) {
+            .cleanHistoryMessages(cId, data.conversation.sentTime, true, null)
+    }
 
+    fun deleteConversation(cId: ConversationIdentifier) {
+        viewModelScope.launch(Dispatcher.WENEXT_THREAD_POOL) {
+            IMService.innerService.getConversation(
+                cId.type,
+                cId.targetId,
+                object : RongIMClient.ResultCallback<Conversation?>() {
+                    override fun onSuccess(conversation: Conversation?) {
+                        if (conversation != null) {
+                            IMService.innerService.setConversationToTop(cId, false, null)
+                            IMService.innerService.removeConversation(cId.type, cId.targetId, null)
+                            IMService.innerService
+                                .cleanHistoryMessages(cId, conversation.sentTime, true, null)
+                        }
                     }
 
-                    override fun onError(errorCode: RongIMClient.ErrorCode) {
+                    override fun onError(e: RongIMClient.ErrorCode?) {
 
                     }
+
                 })
+        }
     }
 
-    fun onReadClick(data: BaseUiConversation) {
+    fun readAllUnreadConversation() {
+        IMService.innerService.getUnreadConversationList(
+            object : IRongCoreCallback.ResultCallback<List<Conversation>>() {
+                override fun onSuccess(conversations: List<Conversation>) {
+                    for (conversation in conversations) {
+                        IMService.innerService.clearMessagesUnreadStatus(
+                            conversation.conversationType,
+                            conversation.targetId,
+                            null
+                        )
+                    }
+                }
+
+                override fun onError(e: IRongCoreEnum.CoreErrorCode?) {
+
+                }
+
+            },
+            *IMConfigCenter.conversationListConfig.getDataProcessor().supportedTypes()
+        )
+    }
+
+    private fun onPinClick(data: BaseUiConversation) {
         val cId = data.conversationIdentifier ?: return
-        IMService.innerService.clearMessagesUnreadStatus(cId, null)
+        setConversationToTop(cId, !data.conversation.isTop)
     }
 
-    fun deleteConversation(data: BaseUiConversation) {
-        //删除会话同时要取消置顶
-        val cId = data.conversationIdentifier?: return
-        IMService.innerService
-            .setConversationToTop(
-                cId,
-                false,
-                null
-            )
-        IMService.innerService
-            .removeConversation(
-                data.conversation.conversationType,
-                data.conversation.targetId,
-                null
-            )
+    private fun onReadClick(data: BaseUiConversation) {
+        val cId = data.conversationIdentifier ?: return
+        IMService.innerService.clearMessagesUnreadStatus(cId, null)
     }
 
     override fun onCleared() {
@@ -620,7 +695,9 @@ open class ConversationListViewModel : BaseViewModel(),
         IMService.innerService.removeReadReceiptListener(readReceiptListener)
         IMService.innerService.removeOnRecallMessageListener(onRecallMessageListener)
         IMService.innerService.removeConversationEventListener(conversationEventListener)
-        IMService.innerService.removeSyncConversationReadStatusListeners(syncConversationReadStatusListener)
+        IMService.innerService.removeSyncConversationReadStatusListeners(
+            syncConversationReadStatusListener
+        )
         IMService.innerService.removeCancelSendMediaMessageListener(cancelSendMediaMessageListener)
     }
 
@@ -642,29 +719,37 @@ open class ConversationListViewModel : BaseViewModel(),
             LogUtil.e(TAG_IM_CONVERSATION_VM, "rc_is_show_warning_notification is disabled.")
             return
         }
-        if (status == RongIMClient.ConnectionStatusListener.ConnectionStatus.NETWORK_UNAVAILABLE) {
-            content = getCompatString(R.string.rc_conversation_list_notice_network_unavailable)
-            resId = R.drawable.im_ic_error_notice
-        } else if (status == RongIMClient.ConnectionStatusListener.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT) {
-            content = getCompatString(R.string.rc_conversation_list_notice_kicked)
-            resId = R.drawable.im_ic_error_notice
-        } else if (status == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED) {
-            isShowContent = false
-        } else if (status == RongIMClient.ConnectionStatusListener.ConnectionStatus.UNCONNECTED) {
-            content = getCompatString(R.string.rc_conversation_list_notice_disconnect)
-            resId = R.drawable.im_ic_error_notice
-        } else if (status == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTING || status == RongIMClient.ConnectionStatusListener.ConnectionStatus.SUSPEND) {
-            content = getCompatString(R.string.rc_conversation_list_notice_connecting)
-            resId = R.drawable.im_conversationlist_notice_connecting_animated
-        } else if (status == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTION_STATUS_PROXY_UNAVAILABLE) {
-            content = getCompatString(R.string.rc_conversation_list_notice_proxy_unavailable)
-            resId = R.drawable.im_ic_error_notice
-        } else {
-            if (preConnectionStatus == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTION_STATUS_PROXY_UNAVAILABLE) {
-                return
+        when (status) {
+            RongIMClient.ConnectionStatusListener.ConnectionStatus.NETWORK_UNAVAILABLE -> {
+                content = getCompatString(R.string.rc_conversation_list_notice_network_unavailable)
+                resId = R.drawable.im_ic_error_notice
+            }
+            RongIMClient.ConnectionStatusListener.ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT -> {
+                content = getCompatString(R.string.rc_conversation_list_notice_kicked)
+                resId = R.drawable.im_ic_error_notice
+            }
+            RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED -> {
+                isShowContent = false
+            }
+            RongIMClient.ConnectionStatusListener.ConnectionStatus.UNCONNECTED -> {
+                content = getCompatString(R.string.rc_conversation_list_notice_disconnect)
+                resId = R.drawable.im_ic_error_notice
+            }
+            RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTING, RongIMClient.ConnectionStatusListener.ConnectionStatus.SUSPEND -> {
+                content = getCompatString(R.string.rc_conversation_list_notice_connecting)
+                resId = R.drawable.im_conversationlist_notice_connecting_animated
+            }
+            RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTION_STATUS_PROXY_UNAVAILABLE -> {
+                content = getCompatString(R.string.rc_conversation_list_notice_proxy_unavailable)
+                resId = R.drawable.im_ic_error_notice
+            }
+            else -> {
+                if (preConnectionStatus == RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTION_STATUS_PROXY_UNAVAILABLE) {
+                    return
+                }
+                content = getCompatString(R.string.rc_conversation_list_notice_network_unavailable)
+                resId = R.drawable.im_ic_error_notice
             }
-            content = getCompatString(R.string.rc_conversation_list_notice_network_unavailable)
-            resId = R.drawable.im_ic_error_notice
         }
 
         noticeContent.content = content
@@ -712,24 +797,6 @@ open class ConversationListViewModel : BaseViewModel(),
         }
     }
 
-    fun readAllUnreadConversation() {
-        IMService.innerService.getUnreadConversationList(
-            object : IRongCoreCallback.ResultCallback<List<Conversation>>() {
-                override fun onSuccess(conversations: List<Conversation>) {
-                    for (conversation in conversations) {
-                        IMService.innerService.clearMessagesUnreadStatus(conversation.conversationType, conversation.targetId, null)
-                    }
-                }
-
-                override fun onError(e: IRongCoreEnum.CoreErrorCode?) {
-
-                }
-
-            },
-            *IMConfigCenter.conversationListConfig.getDataProcessor().supportedTypes()
-        )
-    }
-
     private inner class GetConversationListRunnable(
         private val viewModel: ConversationListViewModel,
         val loadMore: Boolean,

+ 2 - 2
frame/imkit/src/main/java/com/adealink/frame/imkit/feature/refrence/ReferenceMessageItemProvider.kt

@@ -242,7 +242,7 @@ class ReferenceMessageItemProvider : BaseMessageItemProvider<ReferenceMessage>()
             var result = false
             val str = link.lowercase(Locale.getDefault())
             if (str.startsWith("http") || str.startsWith("https")) {
-                IMService.innerService.config.routeToWebActivity(link)
+                IMService.innerService.config.routeUrl(link)
                 result = true
             }
             result
@@ -382,7 +382,7 @@ class ReferenceMessageItemProvider : BaseMessageItemProvider<ReferenceMessage>()
         textView.text = uiMessage.referenceContentSpannable
         holder.setOnClickListener(
             R.id.im_msg_tv_reference_content
-        ) { IMService.innerService.config.routeToWebActivity(content.url) }
+        ) { IMService.innerService.config.routeUrl(content.url) }
         holder.setOnLongClickListener(
             R.id.im_msg_tv_reference_content
         ) { parentHolder.getView<View>(R.id.im_content)?.performLongClick() == true }

+ 23 - 0
frame/imkit/src/main/java/com/adealink/frame/imkit/model/MessageBlockRes.kt

@@ -0,0 +1,23 @@
+package com.adealink.frame.imkit.model
+
+import com.google.gson.annotations.GsonNullable
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory.Adapter::class)
+data class MessageBlockRlt(
+    @GsonNullable
+    @SerializedName("result")
+    val result: MessageBlockRes?,
+)
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory.Adapter::class)
+data class MessageBlockRes(
+    @SerializedName("code")
+    val code: Int,
+    @SerializedName("message")
+    val message: String,
+    @SerializedName("sucessed")
+    val success: Boolean
+)

+ 130 - 0
frame/imkit/src/main/java/com/adealink/frame/imkit/model/OfficialMessage.kt

@@ -0,0 +1,130 @@
+package com.adealink.frame.imkit.model
+
+import android.os.Parcel
+import android.os.Parcelable
+import io.rong.common.ParcelUtils
+import io.rong.imlib.MessageTag
+import io.rong.imlib.model.MessageContent
+import org.json.JSONException
+import org.json.JSONObject
+import java.io.UnsupportedEncodingException
+
+/**
+ * 官方消息类
+ */
+@MessageTag(value = "app:official", flag = MessageTag.ISCOUNTED)
+class OfficialMessage : MessageContent {
+    var title: String? = null
+    var content: String? = null
+    var bannerUrl: String? = null
+    var bannerUrlJumpLink: String? = null
+    var jumpLink: String? = null
+    var jumpLinkText: String? = null
+
+    // 对外构造方法
+    private constructor()
+
+    constructor(parcel: Parcel) : this() {
+        title = ParcelUtils.readFromParcel(parcel)
+        content = ParcelUtils.readFromParcel(parcel)
+        bannerUrl = ParcelUtils.readFromParcel(parcel)
+        bannerUrlJumpLink = ParcelUtils.readFromParcel(parcel)
+        jumpLink = ParcelUtils.readFromParcel(parcel)
+        jumpLinkText = ParcelUtils.readFromParcel(parcel)
+    }
+
+    /**
+     * 将本地消息对象序列化为消息数据。
+     *
+     * @return 消息数据。
+     */
+    override fun encode(): ByteArray? {
+        val jsonObj = super.getBaseJsonObject()
+        try {
+            //将所有自定义消息的内容,都序列化至 json 对象中
+            jsonObj.putOpt("title", this.title)
+            jsonObj.put("content", this.content)
+            jsonObj.put("bannerUrl", this.bannerUrl)
+            jsonObj.put("bannerUrlJumpLink", this.bannerUrlJumpLink)
+            jsonObj.put("jumpLink", this.jumpLink)
+            jsonObj.put("jumpLinkText", this.jumpLinkText)
+        } catch (e: JSONException) {
+
+        }
+
+        try {
+            return jsonObj.toString().toByteArray(charset("UTF-8"))
+        } catch (e: UnsupportedEncodingException) {
+
+        }
+        return null
+    }
+
+    /** 创建 OfficialMessage(byte[] data) 带有 byte[] 的构造方法用于解析消息内容.  */
+    constructor(data: ByteArray?) {
+        if (data == null) {
+            return
+        }
+        var jsonStr: String? = null
+        try {
+            jsonStr = String(data, charset("UTF-8"))
+        } catch (e: UnsupportedEncodingException) {
+
+        }
+        if (jsonStr == null) {
+            return
+        }
+
+        try {
+            val jsonObj = JSONObject(jsonStr)
+            super.parseBaseJsonObject(jsonObj)
+            // 将所有自定义变量从收到的 json 解析并赋值
+            if (jsonObj.has("title")) {
+                this.title = jsonObj.optString("title")
+            }
+            if (jsonObj.has("content")) {
+                this.content = jsonObj.optString("content")
+            }
+            if (jsonObj.has("bannerUrl")) {
+                this.bannerUrl = jsonObj.optString("bannerUrl")
+            }
+            if (jsonObj.has("bannerUrlJumpLink")) {
+                this.bannerUrlJumpLink = jsonObj.optString("bannerUrlJumpLink")
+            }
+            if (jsonObj.has("jumpLink")) {
+                this.jumpLink = jsonObj.optString("jumpLink")
+            }
+            if (jsonObj.has("jumpLinkText")) {
+                this.jumpLinkText = jsonObj.optString("jumpLinkText")
+            }
+        } catch (e: JSONException) {
+
+        }
+    }
+
+    override fun describeContents(): Int {
+        return 0
+    }
+
+    override fun writeToParcel(dest: Parcel, flags: Int) {
+        // 对消息属性进行序列化,将类的数据写入外部提供的 Parcel 中,此处以需要序列化"用户信息","@信息" 等为例
+        super.writeToBaseInfoParcel(dest)
+        ParcelUtils.writeToParcel(dest, this.title)
+        ParcelUtils.writeToParcel(dest, this.content)
+        ParcelUtils.writeToParcel(dest, this.bannerUrl)
+        ParcelUtils.writeToParcel(dest, this.bannerUrlJumpLink)
+        ParcelUtils.writeToParcel(dest, this.jumpLink)
+        ParcelUtils.writeToParcel(dest, this.jumpLinkText)
+    }
+
+    companion object CREATOR : Parcelable.Creator<OfficialMessage> {
+        override fun createFromParcel(parcel: Parcel): OfficialMessage {
+            return OfficialMessage(parcel)
+        }
+
+        override fun newArray(size: Int): Array<OfficialMessage?> {
+            return arrayOfNulls(size)
+        }
+    }
+
+}

+ 21 - 0
frame/imkit/src/main/java/com/adealink/frame/imkit/model/OfficialMessageRealContent.kt

@@ -0,0 +1,21 @@
+package com.adealink.frame.imkit.model
+
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+
+@JsonAdapter(ExtReflectiveTypeAdapterFactory.Adapter::class)
+data class OfficialMessageRealContent(
+    @SerializedName("title")
+    val title: String,
+    @SerializedName("content")
+    val content: String,
+    @SerializedName("bannerUrl")
+    var bannerUrl: String,
+    @SerializedName("bannerUrlJumpLink")
+    var bannerUrlJumpLink: String,
+    @SerializedName("jumpLink")
+    var jumpLink: String,
+    @SerializedName("jumpLinkText")
+    var jumpLinkText: String,
+)

+ 4 - 2
frame/imkit/src/main/java/com/adealink/frame/imkit/model/Tags.kt

@@ -1,12 +1,14 @@
 package com.adealink.frame.imkit.model
 
 const val TAG_IM = "tag_im"
+const val TAG_IM_UI = "${TAG_IM}_ui"
 const val TAG_IM_SERVICE = "${TAG_IM}_service"
 const val TAG_IM_MSG_PROVIDER = "${TAG_IM}_msg_provider"
 const val TAG_IM_CONVERSATION_VM = "${TAG_IM}_conversation_vm"
-const val TAG_IM_MESSAGE_VM = "${TAG_IM}_message_vm"
+const val TAG_IM_MSG_VM = "${TAG_IM}_sg_vm"
 const val TAG_IM_KEYBOARD = "${TAG_IM}_keyboard"
 const val TAG_IM_PROCESSOR = "${TAG_IM}_processor"
 const val TAG_IM_RESEND = "${TAG_IM}_resend"
 const val TAG_IM_UNREAD = "${TAG_IM}_unread"
-const val TAG_IM_REFERENCE = "${TAG_IM}_reference"
+const val TAG_IM_REFERENCE = "${TAG_IM}_reference"
+const val TAG_IM_MSG_BLOCK = "${TAG_IM}_msg_block"

+ 16 - 0
frame/imkit/src/main/java/com/adealink/frame/imkit/model/TextAndJumpLink.kt

@@ -0,0 +1,16 @@
+package com.adealink.frame.imkit.model
+
+import android.os.Parcelable
+import com.google.gson.annotations.JsonAdapter
+import com.google.gson.annotations.SerializedName
+import com.google.gson.internal.bind.ExtReflectiveTypeAdapterFactory
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+@JsonAdapter(ExtReflectiveTypeAdapterFactory::class)
+data class TextAndJumpLink(
+    @SerializedName("jumpLink")
+    val jumpLink: String,
+    @SerializedName("text")
+    val text: String,
+): Parcelable

+ 1 - 1
frame/imkit/src/main/java/com/adealink/frame/imkit/model/UiMessage.kt

@@ -78,7 +78,7 @@ class UiMessage(private var message: Message) : UiBaseBean() {
             if (message.messageDirection == MessageDirection.SEND) {
                 message.senderUserId = RongIMClient.getInstance().currentUserId
             } else {
-                LogUtil.e(TAG_IM, "Invalid message with empty senderUserId!")
+                LogUtil.e(TAG_IM_UI, "Invalid message with empty senderUserId!")
                 return
             }
         }

BIN=BIN
frame/imkit/src/main/res/drawable-ldrtl-xhdpi/im_ic_bubble_left.9.png


+ 2 - 2
frame/imkit/src/main/res/layout/im_item_rich_content_message.xml

@@ -4,8 +4,7 @@
     xmlns:tools="http://schemas.android.com/tools"
     android:id="@id/im_layout"
     android:layout_width="239dp"
-    android:layout_height="wrap_content"
-    android:background="@android:color/white">
+    android:layout_height="wrap_content">
 
     <com.adealink.frame.image.view.NetworkImageView
         android:id="@+id/im_img"
@@ -22,6 +21,7 @@
         android:layout_height="wrap_content"
         android:layout_marginHorizontal="12dp"
         android:layout_marginTop="6dp"
+        app:layout_goneMarginTop="12dp"
         android:background="@android:color/transparent"
         android:ellipsize="end"
         android:gravity="center_vertical|start"

+ 4 - 0
module/message/src/main/AndroidManifest.xml

@@ -20,5 +20,9 @@
             android:name=".conversation.ConversationActivity"
             android:screenOrientation="portrait"
             android:theme="@style/AppTheme" />
+        <activity
+            android:name=".conversation.setting.ConversationSettingActivity"
+            android:screenOrientation="portrait"
+            android:theme="@style/AppTheme" />
     </application>
 </manifest>

+ 10 - 6
module/message/src/main/java/com/adealink/weparty/message/MessageHomeFragment.kt

@@ -3,9 +3,9 @@ package com.adealink.weparty.message
 import androidx.core.view.updateLayoutParams
 import androidx.fragment.app.Fragment
 import androidx.fragment.app.activityViewModels
-import androidx.fragment.app.viewModels
 import com.adealink.frame.aab.util.getCompatString
 import com.adealink.frame.mvvm.view.viewBinding
+import com.adealink.frame.router.Router
 import com.adealink.frame.router.annotation.RouterUri
 import com.adealink.frame.util.DisplayUtil
 import com.adealink.frame.util.onClick
@@ -13,17 +13,18 @@ import com.adealink.weparty.commonui.BaseFragment
 import com.adealink.weparty.commonui.recycleview.adapter.BaseTabFragmentStateAdapter
 import com.adealink.weparty.commonui.widget.CommonDialog
 import com.adealink.weparty.commonui.widget.EmptyFragment
-import com.adealink.weparty.message.conversationlist.viewmodel.WeConversationListViewModel
-import com.adealink.weparty.message.databinding.FragmentMessageHomeBinding
+import com.adealink.weparty.message.conversationlist.viewmodel.WeConversationSettingViewModel
 import com.adealink.weparty.message.data.MessageHomeTabType
-import com.adealink.weparty.message.datasource.remote.MessageLocalService
+import com.adealink.weparty.message.databinding.FragmentMessageHomeBinding
 import com.adealink.weparty.module.message.Message
+import com.adealink.weparty.module.search.Search
+import com.adealink.weparty.ui.listener.setOnGameClick
 import com.adealink.weparty.R as APP_R
 
 @RouterUri(path = [Message.Home.PATH], desc = "消息首页")
 class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
     private val binding by viewBinding(FragmentMessageHomeBinding::bind)
-    private val conversationListViewModel by activityViewModels<WeConversationListViewModel>()
+    private val conversationListViewModel by activityViewModels<WeConversationSettingViewModel>()
 
     override fun initViews() {
         super.initViews()
@@ -40,7 +41,6 @@ class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
             currentItem = 0
         )
 
-        //增加一键已读二次提醒弹窗(仅在首次操作时出现)
         binding.btnClearUnreadMsg.onClick(5000) {
             CommonDialog.Builder()
                 .message(getCompatString(R.string.message_clear_all_tips))
@@ -52,6 +52,10 @@ class MessageHomeFragment : BaseFragment(R.layout.fragment_message_home) {
                 .build()
                 .show(childFragmentManager)
         }
+        binding.btnSearch.setOnClickListener {
+            val activity = activity ?: return@setOnClickListener
+            Router.build(activity, Search.Search.PATH).start()
+        }
     }
 
     inner class PageAdapter : BaseTabFragmentStateAdapter(this) {

+ 28 - 1
module/message/src/main/java/com/adealink/weparty/message/conversation/ConversationFragment.kt

@@ -16,14 +16,17 @@ import androidx.lifecycle.Observer
 import androidx.recyclerview.widget.LinearLayoutManager
 import androidx.recyclerview.widget.RecyclerView
 import com.adealink.frame.aab.util.getCompatColor
+import com.adealink.frame.base.AppBaseInfo
 import com.adealink.frame.base.fastLazy
 import com.adealink.frame.ext.isViewBindingValid
 import com.adealink.frame.imkit.MessageItemLongClickAction
 import com.adealink.frame.imkit.config.IMConfigCenter
 import com.adealink.frame.imkit.conversation.MessageListAdapter
 import com.adealink.frame.imkit.conversation.extension.ExtensionViewModel
+import com.adealink.frame.imkit.conversation.messagelist.provider.MessageClickType
 import com.adealink.frame.imkit.conversation.messagelist.status.MessageProcessor
 import com.adealink.frame.imkit.conversation.viewmodel.MessageItemLongClickBean
+import com.adealink.frame.imkit.conversationlist.model.BaseUiConversation
 import com.adealink.frame.imkit.event.Event
 import com.adealink.frame.imkit.event.uievent.PageDestroyEvent
 import com.adealink.frame.imkit.event.uievent.PageEvent
@@ -81,6 +84,7 @@ import com.scwang.smart.refresh.layout.listener.OnLoadMoreListener
 import com.scwang.smart.refresh.layout.listener.OnRefreshListener
 import io.rong.imlib.model.Conversation.ConversationType
 import io.rong.imlib.model.ConversationIdentifier
+import io.rong.push.RongPushClient
 import java.text.MessageFormat
 import kotlin.math.max
 import com.adealink.frame.imkit.R as IM_KIT_R
@@ -294,6 +298,18 @@ class ConversationFragment : BaseFragment(R.layout.fragment_conversation),
         binding.backIv.setOnClickListener {
             activity?.finish()
         }
+        if (!isSystemTarget(toUid) || !AppBaseInfo.isRelease) {
+            binding.moreIv.show()
+            binding.moreIv.setOnClickListener {
+                val act = activity ?: return@setOnClickListener
+                Router.build(act, Message.Conversation.SETTING)
+                    .putExtra(Message.Common.EXTRA_TO_UID, toUid)
+                    .putExtra(Message.Common.EXTRA_CONVERSATION_TYPE, conversationType)
+                    .start()
+            }
+        } else {
+            binding.moreIv.gone()
+        }
         binding.messageList.layoutManager = linearLayoutManager
         binding.refreshLayout.setOnTouchListener(
             View.OnTouchListener { _, _ ->
@@ -471,7 +487,18 @@ class ConversationFragment : BaseFragment(R.layout.fragment_conversation),
     }
 
     override fun onViewClick(clickType: Int, data: UiMessage) {
-        messageViewModel.onViewClick(clickType, data)
+        when (clickType) {
+            MessageClickType.USER_PORTRAIT_CLICK -> {
+                val act = activity ?: return
+                val uid = data.getUserInfo().id.toLongOrNull()
+                Router.build(act, Profile.UserProfile.PATH).putExtra(
+                    Profile.Common.EXTRA_UID, uid
+                ).start()
+            }
+            else -> {
+                messageViewModel.onViewClick(clickType, data)
+            }
+        }
     }
 
     override fun onViewLongClick(clickType: Int, data: UiMessage): Boolean {

+ 1 - 1
module/message/src/main/java/com/adealink/weparty/message/conversation/comp/ConversationTargetInfoComp.kt

@@ -28,7 +28,7 @@ class ConversationTargetInfoComp(
         } else {
             profileViewModel?.getUidUserInfo(targetId, true)?.observe(viewLifecycleOwner) {
                 it.onSuccess { userInfo ->
-                    binding.nameTv.text = userInfo.name
+                    binding.nameTv.setDisplayName(userInfo.uid, userInfo.name)
                 }
             }
         }

+ 9 - 0
module/message/src/main/java/com/adealink/weparty/message/conversation/provider/TargetInfoProvider.kt

@@ -8,6 +8,8 @@ import com.adealink.frame.imkit.model.UiMessage
 import com.adealink.frame.imkit.widget.adapter.IViewProviderListener
 import com.adealink.frame.imkit.widget.adapter.ViewHolder
 import com.adealink.frame.imkit.conversation.messagelist.provider.BaseNotificationMessageItemProvider
+import com.adealink.weparty.commonui.ext.gone
+import com.adealink.weparty.commonui.ext.show
 import com.adealink.weparty.message.R
 import com.adealink.weparty.message.conversation.listener.IConversationProviderCallback
 import com.adealink.weparty.message.conversation.message.TargetInfoMessage
@@ -48,6 +50,13 @@ class TargetInfoProvider(private val callback: IConversationProviderCallback?):
         callback?.getTargetInfo(message.targetId) { targetInfo ->
             val userInfo = targetInfo?.userInfo ?: return@getTargetInfo
             binding.avatar.setImageUrl(userInfo.url)
+            val nationFlag = userInfo.flag
+            if (nationFlag.isNullOrEmpty()) {
+                binding.nationalFlag.gone()
+            } else {
+                binding.nationalFlag.show()
+                binding.nationalFlag.setImageUrl(userInfo.flag)
+            }
             when (userInfo.gender) {
                 Gender.FEMALE.gender -> {
                     binding.ageSex.setBackgroundResource(APP_R.drawable.common_sex_female_bg)

+ 274 - 0
module/message/src/main/java/com/adealink/weparty/message/conversation/setting/ConversationSettingActivity.kt

@@ -0,0 +1,274 @@
+package com.adealink.weparty.message.conversation.setting
+
+import android.os.Bundle
+import android.util.TypedValue
+import android.view.LayoutInflater
+import androidx.activity.viewModels
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.core.view.updateLayoutParams
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.base.fastLazy
+import com.adealink.frame.mvvm.view.viewBinding
+import com.adealink.frame.router.Router
+import com.adealink.frame.router.annotation.BindExtra
+import com.adealink.frame.router.annotation.RouterUri
+import com.adealink.frame.util.DisplayUtil
+import com.adealink.weparty.commonui.BaseActivity
+import com.adealink.weparty.commonui.dialogfragment.BaseDialogFragment
+import com.adealink.weparty.commonui.ext.onSuccess
+import com.adealink.weparty.commonui.widget.CommonDialog
+import com.adealink.weparty.commonui.widget.switchbutton.SwitchButton
+import com.adealink.weparty.message.R
+import com.adealink.weparty.message.conversation.setting.dialog.ModifyRemarkDescDialog
+import com.adealink.weparty.message.conversationlist.viewmodel.WeConversationSettingViewModel
+import com.adealink.weparty.message.databinding.ActivityConversationSettingBinding
+import com.adealink.weparty.message.databinding.LayoutMessageSettingSwitchBinding
+import com.adealink.weparty.module.message.Message
+import com.adealink.weparty.module.profile.Profile
+import com.adealink.weparty.module.profile.ProfileModule
+import com.qmuiteam.qmui.widget.grouplist.QMUICommonListItemView
+import com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView
+import com.qmuiteam.qmui.widget.util.QMUIStatusBarHelper
+import io.rong.imlib.model.Conversation.ConversationType
+import io.rong.imlib.model.ConversationIdentifier
+import com.adealink.weparty.R as APP_R
+
+@RouterUri(path = [Message.Conversation.SETTING], desc = "会话设置页")
+class ConversationSettingActivity : BaseActivity() {
+    private val binding by viewBinding(ActivityConversationSettingBinding::inflate)
+
+    @BindExtra(name = Message.Common.EXTRA_TO_UID, desc = "聊天对方的uid", must = true)
+    var toUid: Long = 0
+
+    @BindExtra(name = Message.Common.EXTRA_CONVERSATION_TYPE, desc = "会话类型")
+    var conversationType: Int = ConversationType.NONE.value
+
+    private val profileViewModel by fastLazy { ProfileModule.getProfileViewModel(this) }
+    private val conversationListViewModel by viewModels<WeConversationSettingViewModel>()
+    private var conversationIdentifier: ConversationIdentifier? = null
+    private var remark: String? = null
+    private var desc: String? = null
+
+    private val topSwitchBinding by fastLazy {
+        LayoutMessageSettingSwitchBinding.inflate(
+            LayoutInflater.from(this), null, false
+        )
+    }
+
+    private val blockSwitchBinding by fastLazy {
+        LayoutMessageSettingSwitchBinding.inflate(
+            LayoutInflater.from(this), null, false
+        )
+    }
+
+    override fun onBeforeCreate() {
+        super.onBeforeCreate()
+        Router.bind(this)
+        val conversationType = ConversationType.setValue(conversationType)
+        this.conversationIdentifier = ConversationIdentifier.obtain(conversationType, toUid.toString(), "")
+    }
+
+    override fun initViews() {
+        super.initViews()
+        QMUIStatusBarHelper.setStatusBarLightMode(this)
+        setContentView(binding.root)
+        binding.topBar.updateLayoutParams<ConstraintLayout.LayoutParams> {
+            topMargin = DisplayUtil.getStatusBarHeight(this@ConversationSettingActivity)
+        }
+        binding.topTargetUserCl.setOnClickListener {
+            Router.build(this, Profile.UserProfile.PATH)
+                .putExtra(Profile.Common.EXTRA_UID, toUid)
+                .start()
+        }
+
+        val itemHeight = DisplayUtil.dp2px(50f)
+        val textSize = DisplayUtil.sp2px(14f).toFloat()
+        //备注
+        val remarkItem: QMUICommonListItemView = binding.topGroupListView.createItemView(
+            null,
+            getCompatString(R.string.message_write_remark),
+            "",
+            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,
+            itemHeight
+        ).apply {
+            setBackgroundResource(APP_R.drawable.common_white_top_radius_12_bg)
+            textView.setTextSize(
+                TypedValue.COMPLEX_UNIT_PX,
+                textSize
+            )
+        }
+        //描述
+        val descItem: QMUICommonListItemView = binding.topGroupListView.createItemView(
+            null,
+            getCompatString(R.string.message_description),
+            "",
+            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,
+            itemHeight
+        ).apply {
+            setBackgroundResource(APP_R.drawable.common_white_bottom_radius_12_bg)
+            textView.setTextSize(
+                TypedValue.COMPLEX_UNIT_PX,
+                textSize
+            )
+        }
+        QMUIGroupListView.newSection(this).apply {
+            addItemView(remarkItem) {
+                Router.getRouterInstance<BaseDialogFragment>(Message.Conversation.MODIFY_REMARK_DESC)?.apply {
+                    arguments = Bundle().apply {
+                        putString(Message.Common.EXTRA_REMARK_DESC, remark)
+                        putInt(Message.Common.EXTRA_TYPE, ModifyRemarkDescDialog.REMARK_TYPE)
+                        putLong(Message.Common.EXTRA_TO_UID, toUid)
+                    }
+                }?.show(supportFragmentManager)
+            }
+            addItemView(descItem) {
+                Router.getRouterInstance<BaseDialogFragment>(Message.Conversation.MODIFY_REMARK_DESC)?.apply {
+                    arguments = Bundle().apply {
+                        putString(Message.Common.EXTRA_REMARK_DESC, desc)
+                        putInt(Message.Common.EXTRA_TYPE, ModifyRemarkDescDialog.DESC_TYPE)
+                        putLong(Message.Common.EXTRA_TO_UID, toUid)
+                    }
+                }?.show(supportFragmentManager)
+            }
+            setUseTitleViewForSectionSpace(false)
+            setShowSeparator(false)
+            addTo(binding.topGroupListView)
+        }
+
+        //删除历史消息
+        val deleteHistoryItem: QMUICommonListItemView = binding.topGroupListView.createItemView(
+            null,
+            getCompatString(R.string.message_delete_chat_history),
+            "",
+            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,
+            itemHeight
+        ).apply {
+            setBackgroundResource(APP_R.drawable.common_white_radius_12_bg)
+            textView.setTextSize(
+                TypedValue.COMPLEX_UNIT_PX,
+                textSize
+            )
+        }
+        QMUIGroupListView.newSection(this).apply {
+            addItemView(deleteHistoryItem) {
+                CommonDialog.Builder()
+                    .message(getCompatString(R.string.message_conversation_delete_tip))
+                    .onPositive {
+                        val cId = conversationIdentifier ?: return@onPositive
+                        conversationListViewModel.deleteConversation(cId)
+                    }
+                    .setShowDefaultCancel(true)
+                    .show(supportFragmentManager)
+            }
+            setUseTitleViewForSectionSpace(false)
+            setShowSeparator(false)
+            addTo(binding.centerGroupListView)
+        }
+
+        //Top
+        val topItem: QMUICommonListItemView = binding.topGroupListView.createItemView(
+            null,
+            getCompatString(R.string.message_top),
+            "",
+            QMUICommonListItemView.ACCESSORY_TYPE_CUSTOM,
+            itemHeight
+        ).apply {
+            addAccessoryCustomView(topSwitchBinding.root)
+            setBackgroundResource(APP_R.drawable.common_white_top_radius_12_bg)
+            textView.setTextSize(
+                TypedValue.COMPLEX_UNIT_PX,
+                textSize
+            )
+        }
+        topSwitchBinding.root.checkChangedListener =
+            object : SwitchButton.OnCheckChangedListener {
+                override fun onChecked(isChecked: Boolean, fromUser: Boolean) {
+                    onTopSwitchChange(isChecked, fromUser)
+                }
+            }
+
+        //Block
+        val blockItem: QMUICommonListItemView = binding.topGroupListView.createItemView(
+            null,
+            getCompatString(R.string.message_block),
+            "",
+            QMUICommonListItemView.ACCESSORY_TYPE_CUSTOM,
+            itemHeight
+        ).apply {
+            addAccessoryCustomView(blockSwitchBinding.root)
+            textView.setTextSize(
+                TypedValue.COMPLEX_UNIT_PX,
+                textSize
+            )
+        }
+        blockSwitchBinding.root.checkChangedListener =
+            object : SwitchButton.OnCheckChangedListener {
+                override fun onChecked(isChecked: Boolean, fromUser: Boolean) {
+                    onBlockSwitchChange(isChecked, fromUser)
+                }
+            }
+        //举报用户
+        val reportUserItem: QMUICommonListItemView = binding.topGroupListView.createItemView(
+            null,
+            getCompatString(R.string.message_report_user),
+            "",
+            QMUICommonListItemView.ACCESSORY_TYPE_CHEVRON,
+            itemHeight
+        ).apply {
+            setBackgroundResource(APP_R.drawable.common_white_bottom_radius_12_bg)
+            textView.setTextSize(
+                TypedValue.COMPLEX_UNIT_PX,
+                textSize
+            )
+        }
+        QMUIGroupListView.newSection(this).apply {
+            addItemView(topItem) {}
+            addItemView(blockItem) {}
+            addItemView(reportUserItem) {
+                onReportUserClick()
+            }
+            setUseTitleViewForSectionSpace(false)
+            setShowSeparator(false)
+            addTo(binding.bottomGroupListView)
+        }
+    }
+
+    override fun onResume() {
+        super.onResume()
+        profileViewModel?.getUidUserInfo(toUid, true)?.observe(this) {
+            it.onSuccess { userInfo ->
+                binding.targetUserNameTv.setDisplayName(userInfo.uid, userInfo.name)
+                binding.avatarView.setImageUrl(userInfo.url)
+            }
+        }
+        profileViewModel?.getUserRemarkAndDesc(toUid)?.observe(this) {
+            it.onSuccess { data ->
+                this.remark = data.remark
+                this.desc = data.desc
+            }
+        }
+        val cId = conversationIdentifier ?: return
+        conversationListViewModel.isConversationToTop(cId).observe(this) {
+            topSwitchBinding.root.setChecked(it)
+        }
+    }
+
+    private fun onTopSwitchChange(isChecked: Boolean, fromUser: Boolean) {
+        if (!fromUser) {
+            return
+        }
+        val cId = conversationIdentifier ?: return
+        conversationListViewModel.setConversationToTop(cId, isChecked)
+    }
+
+    private fun onBlockSwitchChange(isChecked: Boolean, fromUser: Boolean) {
+        if (!fromUser) {
+            return
+        }
+    }
+
+    private fun onReportUserClick() {
+
+    }
+
+}

+ 150 - 0
module/message/src/main/java/com/adealink/weparty/message/conversation/setting/dialog/ModifyRemarkDescDialog.kt

@@ -0,0 +1,150 @@
+package com.adealink.weparty.message.conversation.setting.dialog
+
+import android.annotation.SuppressLint
+import android.os.Bundle
+import android.text.Editable
+import android.text.InputFilter
+import android.text.TextWatcher
+import android.view.Gravity
+import com.adealink.frame.aab.util.getCompatString
+import com.adealink.frame.base.fastLazy
+import com.adealink.frame.mvvm.view.viewBinding
+import com.adealink.frame.router.Router
+import com.adealink.frame.router.annotation.BindExtra
+import com.adealink.frame.router.annotation.RouterUri
+import com.adealink.frame.util.noOpDelegate
+import com.adealink.weparty.commonui.ext.onSuccess
+import com.adealink.weparty.commonui.toast.util.showFailedToast
+import com.adealink.weparty.commonui.widget.KeyboardBottomDialogFragment
+import com.adealink.weparty.message.R
+import com.adealink.weparty.message.databinding.DialogModifyRemarkDescDialogBinding
+import com.adealink.weparty.module.message.Message
+import com.adealink.weparty.module.profile.ProfileModule
+
+@RouterUri(path = [Message.Conversation.MODIFY_REMARK_DESC], desc = "更新备注和描述")
+class ModifyRemarkDescDialog : KeyboardBottomDialogFragment(R.layout.dialog_modify_remark_desc_dialog) {
+    override val dimAmount = 0.5f
+
+    private val binding by viewBinding(DialogModifyRemarkDescDialogBinding::bind)
+
+    @BindExtra(name = Message.Common.EXTRA_TO_UID, desc = "聊天对方的uid", must = true)
+    var toUid: Long = 0
+
+    @BindExtra(name = Message.Common.EXTRA_REMARK_DESC, desc = "备注或描述")
+    var remarkDesc: String? = null
+
+    @BindExtra(name = Message.Common.EXTRA_TYPE, desc = "标识备注或者描述")
+    var type: Int = REMARK_TYPE
+
+    private var maxInputNum = MAX_REMARK_CHAR_COUNT
+    private val profileViewModel by fastLazy { ProfileModule.getProfileViewModel(requireActivity()) }
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        Router.bind(this)
+        maxInputNum = if (type == REMARK_TYPE) {
+            MAX_REMARK_CHAR_COUNT
+        } else {
+            MAX_DESC_CHAR_COUNT
+        }
+    }
+
+    override fun initViews() {
+        super.initViews()
+        binding.editText.filters = arrayOf(InputFilter.LengthFilter(maxInputNum))
+        binding.editText.addTextChangedListener(object : TextWatcher by noOpDelegate() {
+
+            @SuppressLint("SetTextI18n")
+            override fun afterTextChanged(s: Editable?) {
+                binding.confirmBtn.isEnabled = s?.toString() != remarkDesc
+                if (s == null) {
+                    binding.tvInputCount.text = "0/$maxInputNum"
+                } else {
+                    val text = s.toString()
+                    if (text.isEmpty()) {
+                        binding.tvInputCount.text = "0/$maxInputNum"
+                    } else {
+                        binding.tvInputCount.text = "${text.length}/$maxInputNum"
+                    }
+                }
+            }
+
+        })
+        binding.cancelBtn.setOnClickListener {
+            dismiss()
+        }
+        binding.confirmBtn.isEnabled = false
+        binding.confirmBtn.setOnClickListener {
+            save()
+        }
+        if (type == REMARK_TYPE) {
+            binding.titleTv.text = getCompatString(R.string.message_write_remark)
+            binding.editText.hint = getCompatString(R.string.message_add_mark_hint)
+        } else {
+            binding.titleTv.text = getCompatString(R.string.message_description)
+            binding.editText.hint = getCompatString(R.string.message_add_desc_hint)
+        }
+
+    }
+
+    override fun loadData() {
+        super.loadData()
+        if (remarkDesc == null) {
+            profileViewModel?.getUserRemarkAndDesc(toUid)?.observe(this) {
+                it.onSuccess { data ->
+                    when(type) {
+                        REMARK_TYPE -> {
+                            remarkDesc = data.remark
+                            binding.editText.setText(data.remark)
+                        }
+                        DESC_TYPE -> {
+                            remarkDesc = data.desc
+                            binding.editText.setText(data.desc)
+                        }
+                    }
+                }
+            }
+            return
+        }
+        binding.editText.setText(remarkDesc)
+    }
+
+    override fun onResume() {
+        super.onResume()
+        binding.editText.requestFocus()
+    }
+
+    private fun save() {
+        val newContent = binding.editText.text?.toString() ?: ""
+        if (remarkDesc == newContent) {
+            dismiss()
+            return
+        }
+        when(type) {
+            REMARK_TYPE -> {
+                profileViewModel?.updateUserNoteName(toUid, newContent)?.observe(viewLifecycleOwner) {
+                    showFailedToast(it)
+                    it.onSuccess {
+                        dismiss()
+                    }
+                }
+            }
+            DESC_TYPE -> {
+                profileViewModel?.updateUserDesc(toUid, newContent)?.observe(viewLifecycleOwner) {
+                    showFailedToast(it)
+                    it.onSuccess {
+                        dismiss()
+                    }
+                }
+            }
+        }
+    }
+
+    companion object {
+        const val MAX_REMARK_CHAR_COUNT = 20
+        const val MAX_DESC_CHAR_COUNT = 300
+
+        const val REMARK_TYPE = 0
+        const val DESC_TYPE = 1
+    }
+}

+ 1 - 0
module/message/src/main/java/com/adealink/weparty/message/conversationlist/ConversationListFragment.kt

@@ -206,6 +206,7 @@ class ConversationListFragment: BaseFragment(R.layout.fragment_conversationlist)
             .onPositive {
                 conversationListViewModel.deleteConversation(data)
             }
+            .setShowDefaultCancel(true)
             .show(childFragmentManager)
     }
 

+ 10 - 0
module/message/src/main/java/com/adealink/weparty/message/conversationlist/viewmodel/WeConversationSettingViewModel.kt

@@ -0,0 +1,10 @@
+package com.adealink.weparty.message.conversationlist.viewmodel
+
+import com.adealink.frame.imkit.conversationlist.viewmodel.ConversationListViewModel
+import io.rong.imlib.model.Conversation
+
+class WeConversationSettingViewModel: ConversationListViewModel(false) {
+    override var supportedTypes = arrayOf(
+        Conversation.ConversationType.PRIVATE,
+    )
+}

+ 5 - 0
module/message/src/main/res/color/message_dialog_cancel_button_color_sel.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/color_weak_button_text" android:state_enabled="true" />
+    <item android:color="@color/color_weak_button_text_disable" android:state_enabled="false" />
+</selector>

+ 5 - 0
module/message/src/main/res/color/message_dialog_normal_button_color_sel.xml

@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:color="@color/color_app_main" android:state_enabled="true" />
+    <item android:color="@color/color_app_main_disable" android:state_enabled="false" />
+</selector>

+ 112 - 0
module/message/src/main/res/layout/activity_conversation_setting.xml

@@ -0,0 +1,112 @@
+<?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"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:background="@color/color_F5F7FA">
+
+    <com.adealink.weparty.commonui.widget.CommonTopBar
+        android:id="@+id/top_bar"
+        android:layout_width="match_parent"
+        android:layout_height="44dp"
+        app:layout_constraintTop_toTopOf="parent"
+        app:top_bar_title="@string/message_chat_setting" />
+
+    <ScrollView
+        android:layout_width="match_parent"
+        android:layout_height="0dp"
+        app:layout_constraintTop_toBottomOf="@id/top_bar"
+        app:layout_constraintBottom_toBottomOf="parent">
+
+        <androidx.constraintlayout.widget.ConstraintLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:paddingHorizontal="12dp">
+
+            <androidx.constraintlayout.widget.ConstraintLayout
+                android:id="@+id/top_target_user_cl"
+                android:layout_width="match_parent"
+                android:layout_height="72dp"
+                android:paddingHorizontal="12dp"
+                android:paddingVertical="16dp"
+                android:layout_marginTop="8dp"
+                app:layout_constraintTop_toTopOf="parent"
+                android:background="@drawable/common_white_radius_12_bg">
+                
+                <com.adealink.weparty.commonui.imageview.AvatarView
+                    android:id="@+id/avatar_view"
+                    android:layout_width="40dp"
+                    android:layout_height="40dp"
+                    app:layout_constraintStart_toStartOf="parent"
+                    app:layout_constraintTop_toTopOf="parent"
+                    app:layout_constraintBottom_toBottomOf="parent" />
+
+                <com.adealink.weparty.module.profile.view.UserNameTextView
+                    android:id="@+id/target_user_name_tv"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:textColor="@color/color_222222"
+                    android:textSize="14sp"
+                    android:layout_marginStart="7dp"
+                    tools:text="Test"
+                    app:layout_constraintStart_toEndOf="@id/avatar_view"
+                    app:layout_constraintTop_toTopOf="parent"
+                    app:layout_constraintBottom_toBottomOf="parent"/>
+
+                <androidx.appcompat.widget.AppCompatImageView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    app:layout_constraintEnd_toEndOf="parent"
+                    app:layout_constraintTop_toTopOf="parent"
+                    app:layout_constraintBottom_toBottomOf="parent"
+                    android:src="@drawable/common_arrow_right_grey_18_ic"/>
+
+            </androidx.constraintlayout.widget.ConstraintLayout>
+
+            <com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView
+                android:id="@+id/topGroupListView"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                app:layout_constraintTop_toBottomOf="@id/top_target_user_cl"/>
+
+            <com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView
+                android:id="@+id/centerGroupListView"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                app:layout_constraintTop_toBottomOf="@id/topGroupListView" />
+
+            <com.qmuiteam.qmui.widget.grouplist.QMUIGroupListView
+                android:id="@+id/bottomGroupListView"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_marginTop="16dp"
+                app:layout_constraintTop_toBottomOf="@id/centerGroupListView" />
+
+        </androidx.constraintlayout.widget.ConstraintLayout>
+
+    </ScrollView>
+
+    <View
+        android:id="@+id/bottom_view"
+        android:layout_width="match_parent"
+        android:layout_height="100dp"
+        android:background="@color/white"
+        app:layout_constraintBottom_toBottomOf="parent"/>
+
+    <com.adealink.weparty.commonui.widget.CommonButton
+        android:id="@+id/follow_btn"
+        android:layout_width="match_parent"
+        android:layout_height="48dp"
+        android:layout_marginTop="12dp"
+        android:textSize="16sp"
+        android:textColor="@color/white"
+        android:gravity="center"
+        android:enabled="false"
+        android:text="@string/common_follow"
+        android:layout_marginHorizontal="12dp"
+        app:layout_constraintTop_toTopOf="@id/bottom_view"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 109 - 0
module/message/src/main/res/layout/dialog_modify_remark_desc_dialog.xml

@@ -0,0 +1,109 @@
+<?xml version="1.0" encoding="utf-8"?>
+<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingBottom="15dp"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:background="@drawable/common_f5f7fa_top_radius_12_bg">
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/top_bar_cl"
+        android:layout_width="match_parent"
+        android:layout_height="44dp"
+        app:layout_constraintTop_toTopOf="parent">
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/cancel_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:textSize="14sp"
+            android:gravity="center"
+            android:paddingHorizontal="16dp"
+            android:textColor="@color/message_dialog_cancel_button_color_sel"
+            android:text="@string/commonui_cancel"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"/>
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/title_tv"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:textSize="14sp"
+            android:gravity="center"
+            android:paddingHorizontal="16dp"
+            android:textColor="@color/color_222222"
+            tools:text="Set Mark"
+            app:layout_constraintStart_toEndOf="@id/cancel_btn"
+            app:layout_constraintEnd_toStartOf="@id/confirm_btn"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"/>
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/confirm_btn"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:textSize="14sp"
+            android:gravity="center"
+            android:paddingHorizontal="16dp"
+            android:enabled="false"
+            android:textColor="@color/message_dialog_normal_button_color_sel"
+            android:text="@string/commonui_confirm"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toBottomOf="parent"/>
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+    <androidx.constraintlayout.widget.ConstraintLayout
+        android:id="@+id/cl_input"
+        android:layout_width="0dp"
+        android:layout_height="wrap_content"
+        android:layout_marginHorizontal="12dp"
+        android:background="@drawable/common_white_radius_12_bg"
+        android:padding="12dp"
+        app:layout_constrainedHeight="true"
+        app:layout_constraintTop_toBottomOf="@id/top_bar_cl"
+        app:layout_constraintEnd_toEndOf="parent"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintVertical_bias="0"
+        app:layout_constraintVertical_chainStyle="packed">
+
+        <androidx.appcompat.widget.AppCompatEditText
+            android:id="@+id/edit_text"
+            style="@style/RtlTextViewStyle"
+            android:layout_width="0dp"
+            android:layout_height="wrap_content"
+            android:background="@null"
+            android:gravity="top|start"
+            android:hint="@string/message_add_mark_hint"
+            android:textColor="@color/color_222222"
+            android:textColorHint="@color/color_AAAAAA"
+            android:textSize="14sp"
+            app:layout_constrainedHeight="true"
+            app:layout_constraintBottom_toTopOf="@id/tv_input_count"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHeight_min="120dp"
+            app:layout_constraintHeight_max="200dp"
+            app:layout_constraintStart_toStartOf="parent"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintVertical_bias="0" />
+
+        <androidx.appcompat.widget.AppCompatTextView
+            android:id="@+id/tv_input_count"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:textColor="@color/color_AAAAAA"
+            android:textSize="14sp"
+            app:layout_constrainedWidth="true"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintEnd_toEndOf="parent"
+            app:layout_constraintHorizontal_bias="1"
+            app:layout_constraintStart_toStartOf="parent" />
+
+    </androidx.constraintlayout.widget.ConstraintLayout>
+
+
+
+</androidx.constraintlayout.widget.ConstraintLayout>

+ 1 - 1
module/message/src/main/res/layout/fragment_message_home.xml

@@ -36,7 +36,7 @@
             android:layout_width="24dp"
             android:layout_height="24dp"
             android:layout_marginEnd="16dp"
-            android:visibility="gone"
+            android:visibility="visible"
             android:src="@drawable/common_search_black_48_ic"
             app:layout_constraintBottom_toBottomOf="parent"
             app:layout_constraintEnd_toStartOf="@id/btn_clear_unread_msg"

+ 1 - 1
module/message/src/main/res/layout/layout_conversation_top_bar_center.xml

@@ -6,7 +6,7 @@
     tools:layout_height="44dp"
     xmlns:app="http://schemas.android.com/apk/res-auto">
 
-    <androidx.appcompat.widget.AppCompatTextView
+    <com.adealink.weparty.module.profile.view.UserNameTextView
         android:id="@+id/name_tv"
         android:layout_width="0dp"
         android:layout_height="wrap_content"

+ 6 - 1
module/message/src/main/res/layout/layout_conversation_user_info_card.xml

@@ -20,6 +20,10 @@
         android:layout_height="10dp"
         android:layout_marginStart="6dp"
         android:visibility="gone"
+        android:scaleType="fitXY"
+        app:layout_constraintHorizontal_bias="0"
+        app:layout_constraintHorizontal_chainStyle="packed"
+        app:layout_constraintVertical_chainStyle="packed"
         app:layout_constraintStart_toEndOf="@id/avatar"
         app:layout_constraintEnd_toStartOf="@id/ageSex"
         app:layout_constraintTop_toTopOf="@id/ageSex"
@@ -38,10 +42,11 @@
         android:layout_marginStart="4dp"
         android:textColor="@color/white"
         android:textSize="12sp"
-        app:layout_constraintVertical_chainStyle="packed"
+        app:layout_constraintHorizontal_bias="0"
         app:layout_constraintStart_toEndOf="@id/national_flag"
         app:layout_constraintTop_toTopOf="@id/avatar"
         app:layout_constraintBottom_toTopOf="@id/profile_tv"
+        app:layout_constraintEnd_toStartOf="@id/more_area"
         tools:text="25"
         tools:visibility="visible" />
 

+ 6 - 0
module/message/src/main/res/layout/layout_message_setting_switch.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<com.adealink.weparty.commonui.widget.switchbutton.SwitchButton xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="48dp"
+    android:layout_height="26dp">
+
+</com.adealink.weparty.commonui.widget.switchbutton.SwitchButton>

+ 9 - 0
module/message/src/main/res/values/strings.xml

@@ -76,4 +76,13 @@
     <string name="message_release_cancel_sending">Release cancel sending</string>
     <string name="message_conversation_delete_tip">After deletion, the chat message history will be cleared</string>
     <string name="message_official_name">Yoki Team</string>
+    <string name="message_chat_setting">Chat Setting</string>
+    <string name="message_write_remark">Write a remark</string>
+    <string name="message_description">Description</string>
+    <string name="message_delete_chat_history">Delete chat history</string>
+    <string name="message_top">Top</string>
+    <string name="message_block">Block</string>
+    <string name="message_report_user">Report User</string>
+    <string name="message_add_mark_hint">Please add a mark</string>
+    <string name="message_add_desc_hint">Please add a description</string>
 </resources>

+ 7 - 0
module/profile/src/main/java/com/adealink/weparty/profile/datasource/ProfileHttpService.kt

@@ -21,6 +21,7 @@ import com.adealink.weparty.module.profile.data.LikePhotoRecordRes
 import com.adealink.weparty.module.profile.data.LikePhotoReq
 import com.adealink.weparty.module.profile.data.RegionUserRes
 import com.adealink.weparty.module.profile.data.RelationshipType
+import com.adealink.weparty.module.profile.data.RemarkAndDescRes
 import com.adealink.weparty.profile.data.ReportUserPhoneModelReq
 import com.adealink.weparty.module.profile.data.UserInfo
 import com.adealink.weparty.module.profile.data.UserPrivilegeInfo
@@ -187,4 +188,10 @@ interface ProfileHttpService {
      */
     @POST("user/remark/add")
     suspend fun addUserRemark(@Body req: AddRemarkAndDescReq): Rlt<Res<Any>>
+
+    /**
+     *  添加备注和描述
+     */
+    @GET("user/remark/get")
+    suspend fun getUserRemarkAndDesc(@Query("toUid") toUid: Long): Rlt<Res<RemarkAndDescRes>>
 }

+ 22 - 0
module/profile/src/main/java/com/adealink/weparty/profile/viewmodel/ProfileViewModel.kt

@@ -28,6 +28,7 @@ import com.adealink.weparty.module.profile.data.GifAvatarHadReview
 import com.adealink.weparty.module.profile.data.GifAvatarLastLevel
 import com.adealink.weparty.module.profile.data.GifFreeTimeType
 import com.adealink.weparty.module.profile.data.RegionUserRes
+import com.adealink.weparty.module.profile.data.RemarkAndDescRes
 import com.adealink.weparty.module.profile.data.SVIPLevel
 import com.adealink.weparty.module.profile.data.SVIPLevel.Companion.isSatisfyLevel
 import com.adealink.weparty.module.profile.data.UserConfigType
@@ -750,4 +751,25 @@ class ProfileViewModel : BaseViewModel(), IProfileViewModel, IProfileListener,
         }
         return liveData
     }
+
+    override fun getUserRemarkAndDesc(uid: Long): LiveData<Rlt<RemarkAndDescRes>> {
+        val liveData = MutableLiveData<Rlt<RemarkAndDescRes>>()
+        viewModelScope.launch {
+            when(val rlt = profileHttpService.getUserRemarkAndDesc(uid)) {
+                is Rlt.Success -> {
+                    val data = rlt.data.data
+                    if (data == null) {
+                        liveData.send(Rlt.Failed(CommonDataNullError()))
+                    } else {
+                        liveData.send(Rlt.Success(data))
+                    }
+                }
+                is Rlt.Failed -> {
+                    liveData.send(rlt)
+                }
+            }
+
+        }
+        return liveData
+    }
 }