From 65ef4810e39fa897e9ebaf3e5e7b20cd0fbc5747 Mon Sep 17 00:00:00 2001 From: max Date: Wed, 29 May 2024 15:26:04 +0800 Subject: [PATCH] =?UTF-8?q?feat=EF=BC=9A=E5=AE=8C=E5=96=84=E6=B8=B8?= =?UTF-8?q?=E6=88=8F=E6=88=BF=E5=85=AC=E5=B1=8F=E5=B1=95=E7=A4=BA=E4=B8=8E?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E5=8F=91=E9=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../avroom/presenter/BaseRoomPresenter.java | 7 +- .../public_chat/PublicChatMessageView.java | 3 - .../PublicChatRoomMessageWidget.kt | 6 +- .../chwl/app/game/core/GameIMEngineAbility.kt | 86 +++++--- .../chwl/app/game/core/GameMessageAbility.kt | 51 ++++- .../chwl/app/game/ui/game/GameViewModel.kt | 12 +- .../ui/game/input/GameMessageInputDialog.kt | 27 ++- .../game/widgets/bottom/GameBottomWidget.kt | 33 ++- .../widgets/message/GameMessageAdapter.kt | 195 +++++++++++++++++- .../message/GameMessageNormalViewHolder.kt | 6 +- .../game/widgets/message/GameMessageWidget.kt | 61 +++++- .../module_game/res/layout/game_activity.xml | 7 +- .../res/layout/game_message_input_dialog.xml | 2 + .../app/public_chat/core/ChatRoomClient.kt | 10 +- .../public_chat/core/ChatRoomClientManager.kt | 13 +- .../ui/message/PublicChatRoomViewModel.kt | 1 + .../support/room/FrameLayoutRoomWidget.kt | 5 + 17 files changed, 453 insertions(+), 72 deletions(-) diff --git a/app/src/main/java/com/chwl/app/avroom/presenter/BaseRoomPresenter.java b/app/src/main/java/com/chwl/app/avroom/presenter/BaseRoomPresenter.java index f94cc825b..f0e07c53b 100644 --- a/app/src/main/java/com/chwl/app/avroom/presenter/BaseRoomPresenter.java +++ b/app/src/main/java/com/chwl/app/avroom/presenter/BaseRoomPresenter.java @@ -565,13 +565,12 @@ public class BaseRoomPresenter extends BaseMvpPresenter @SuppressLint("CheckResult") public void sendPublicChatTextMessage(String message) { if (TextUtils.isEmpty(message)) return; - String sessionId = InitialModel.get().getPublicChatSessionId(); - if (sessionId == null) { + ChatRoomClient client = ChatRoomClientManager.INSTANCE.getPublicChatClient(); + if (client == null) { SingleToastUtil.showToast(R.string.public_chat_not_found); return; } - ChatRoomClient client = ChatRoomClientManager.INSTANCE.getClient(sessionId); - ChatRoomMessage textMessage = ChatRoomMessageBuilder.createChatRoomTextMessage(sessionId, message); + ChatRoomMessage textMessage = ChatRoomMessageBuilder.createChatRoomTextMessage(client.getSessionId(), message); client.sendMessage(textMessage).compose(bindToLifecycle()).subscribe(new BiConsumer() { @Override public void accept(Object o, Throwable throwable) throws Exception { diff --git a/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatMessageView.java b/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatMessageView.java index a8817f7a6..1ea8a8e2a 100644 --- a/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatMessageView.java +++ b/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatMessageView.java @@ -388,9 +388,6 @@ public class PublicChatMessageView extends FrameLayout { } return; } - if (mMessageAdapter.getItemCount() > 0) { - messageListView.smoothScrollToPosition(mMessageAdapter.getItemCount() - 1); - } } public void release() { diff --git a/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatRoomMessageWidget.kt b/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatRoomMessageWidget.kt index f94c09137..7eb4d0833 100644 --- a/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatRoomMessageWidget.kt +++ b/app/src/main/java/com/chwl/app/avroom/public_chat/PublicChatRoomMessageWidget.kt @@ -50,10 +50,8 @@ class PublicChatRoomMessageWidget : FrameLayoutRoomWidget { override fun onStart(roomView: RoomView) { super.onStart(roomView) - val sessionId = InitialModel.get().publicChatSessionId - if (sessionId != null) { - chatRoomClient = ChatRoomClientManager.getClient(sessionId); - } else { + chatRoomClient = ChatRoomClientManager.getPublicChatClient() + if (chatRoomClient == null) { SingleToastUtil.showToast(R.string.public_chat_not_found) } chatRoomClient?.let { diff --git a/app/src/module_game/java/com/chwl/app/game/core/GameIMEngineAbility.kt b/app/src/module_game/java/com/chwl/app/game/core/GameIMEngineAbility.kt index 7fe282e24..e9e887ed8 100644 --- a/app/src/module_game/java/com/chwl/app/game/core/GameIMEngineAbility.kt +++ b/app/src/module_game/java/com/chwl/app/game/core/GameIMEngineAbility.kt @@ -2,52 +2,76 @@ package com.chwl.app.game.core import com.chwl.app.public_chat.core.ChatRoomClient import com.chwl.app.public_chat.core.ChatRoomClientManager +import com.chwl.core.support.listener.ListenerOwner +import com.chwl.core.support.listener.ListenerStore +import com.chwl.core.support.listener.SafeListenerOwner import com.chwl.core.support.room.RoomAbility +import com.chwl.core.support.room.RoomContext +import com.netease.nimlib.sdk.StatusCode +import com.netease.nimlib.sdk.chatroom.ChatRoomMessageBuilder import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage -import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum -import com.netease.nimlib.sdk.msg.model.QueryDirectionEnum +import io.reactivex.Single +import kotlinx.coroutines.flow.MutableStateFlow +import java.lang.IllegalStateException -class GameIMEngineAbility : RoomAbility() { +class GameIMEngineAbility(private val listenerOwner: ListenerOwner = SafeListenerOwner()) : + RoomAbility(), ListenerStore by listenerOwner { - private var client: ChatRoomClient? = null + private var chatRoomClient: ChatRoomClient? = null + + val stateFlow = MutableStateFlow(StatusCode.INVALID) fun enterRoom(sessionId: String) { - client = ChatRoomClientManager.getClient(sessionId) - client?.let { - addDisposable(it.enterChatRoom().subscribe({ - - }, { - - })) + if (chatRoomClient != null && chatRoomClient?.sessionId != sessionId) { + chatRoomClient?.onCleared() } + initClient(sessionId) + } + + private fun initClient(sessionId: String) { + chatRoomClient = ChatRoomClientManager.getClient(sessionId) + chatRoomClient?.let { + safeLaunch { + it.stateFlow.collect { + this@GameIMEngineAbility.stateFlow.emit(it) + } + } + addDisposable(it.messageObservable.subscribe { list -> + listenerOwner.postEvent { it.onReceiveMessage(list) } + }) + enterChatRoom(it) + } + } + + private fun enterChatRoom(client: ChatRoomClient) { + addDisposable(client.enterChatRoom().subscribe({ + }, { + })) } fun sendMessage(message: ChatRoomMessage) { - client?.let { - addDisposable(it.sendMessage(message).subscribe({ - - }, { - - })) + chatRoomClient?.let { + addDisposable(it.sendMessage(message).subscribe({}, {})) } } - private fun requestHistory(chatRoomClient: ChatRoomClient) { - val typeEnums = arrayOf(MsgTypeEnum.text) - addDisposable( - chatRoomClient.requestRemoteMessageType( - 0, - 50, - QueryDirectionEnum.QUERY_OLD, - typeEnums - ).subscribe({ - }, { - it.printStackTrace() - }) - ) + fun buildTextMessage(text: String): ChatRoomMessage? { + val client = chatRoomClient ?: return null + return ChatRoomMessageBuilder.createChatRoomTextMessage(client.sessionId, text) + } + + fun sendMessageRx(message: ChatRoomMessage): Single { + val client = chatRoomClient ?: return Single.error(IllegalStateException("client is NULL")) + return client.sendMessage(message) + } + + override fun onStop(context: RoomContext) { + super.onStop(context) + listenerOwner.removeAllListener() + chatRoomClient?.onCleared() } interface Listener { - fun onStateChange() + fun onReceiveMessage(messages: List) } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/core/GameMessageAbility.kt b/app/src/module_game/java/com/chwl/app/game/core/GameMessageAbility.kt index 7aeacacba..580d483e0 100644 --- a/app/src/module_game/java/com/chwl/app/game/core/GameMessageAbility.kt +++ b/app/src/module_game/java/com/chwl/app/game/core/GameMessageAbility.kt @@ -4,12 +4,23 @@ import com.chwl.core.support.listener.ListenerOwner import com.chwl.core.support.listener.ListenerStore import com.chwl.core.support.listener.SafeListenerOwner import com.chwl.core.support.room.RoomAbility +import com.chwl.core.support.room.RoomContext +import com.netease.nimlib.sdk.chatroom.ChatRoomMessageBuilder +import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage +import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum +import io.reactivex.Single +import java.lang.IllegalStateException class GameMessageAbility(private val listenerOwner: ListenerOwner = SafeListenerOwner()) : - RoomAbility(), ListenerStore by listenerOwner { + RoomAbility(), ListenerStore by listenerOwner, + GameIMEngineAbility.Listener { private val dataList = ArrayList() + private val imEngineAbility + get() = + roomContext?.findAbility(GameIMEngineAbility::class.java.simpleName) + fun getMessageList(): List { return dataList } @@ -21,6 +32,44 @@ class GameMessageAbility(private val listenerOwner: ListenerOwner { + imEngineAbility?.let { + val textMessage = it.buildTextMessage(text) + if (textMessage == null) { + return Single.error(IllegalStateException("message is NULL")) + } else { + return it.sendMessageRx(textMessage).map { + addMessage(textMessage) + } + } + } + return Single.error(IllegalStateException("engine is NULL")) + } + + override fun onStart(context: RoomContext) { + super.onStart(context) + val imEngineAbility = + context.findAbility(GameIMEngineAbility::class.java.simpleName) + imEngineAbility?.addListener(this) + } + + override fun onStop(context: RoomContext) { + super.onStop(context) + listenerOwner.removeAllListener() + } + + override fun onReceiveMessage(messages: List) { + messages.forEach { + onReceiveMessage(it) + } + } + + private fun onReceiveMessage(message: ChatRoomMessage) { + if (message.msgType == MsgTypeEnum.text) { + addMessage(message) + } + } + interface Listener { fun onAddMessage(message: Any) diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt index b922f1eca..4b8bcc692 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt @@ -3,7 +3,9 @@ package com.chwl.app.game.ui.game import androidx.lifecycle.MutableLiveData import com.chwl.app.base.BaseViewModel import com.chwl.app.game.core.GameContext -import com.chwl.core.support.room.RoomContext +import com.chwl.app.game.core.GameIMEngineAbility +import com.chwl.app.public_chat.core.ChatRoomClientManager +import com.chwl.core.initial.InitialModel class GameViewModel : BaseViewModel() { @@ -11,6 +13,14 @@ class GameViewModel : BaseViewModel() { fun init(intent: GameIntent) { val gameContext = GameContext(intent.roomId) + gameContext.performStart() gameContextLiveData.value = gameContext + gameContext.findAbility(GameIMEngineAbility::class.java.simpleName)?.enterRoom( + InitialModel.get().publicChatSessionId) + } + + override fun onCleared() { + super.onCleared() + gameContextLiveData.value?.performStop() } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/input/GameMessageInputDialog.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/input/GameMessageInputDialog.kt index 8dcb83dba..4e1b81440 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/input/GameMessageInputDialog.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/input/GameMessageInputDialog.kt @@ -2,6 +2,7 @@ package com.chwl.app.game.ui.game.input import android.app.Dialog import android.os.Bundle +import android.view.KeyEvent import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -11,12 +12,15 @@ import androidx.lifecycle.repeatOnLifecycle import com.chwl.app.R import com.chwl.app.common.widget.dialog.DialogManager import com.chwl.app.databinding.GameMessageInputDialogBinding +import com.chwl.app.game.core.GameContext +import com.chwl.app.game.core.GameMessageAbility +import com.chwl.core.support.room.RoomContext import com.chwl.library.utils.SingleToastUtil import com.chwl.library.utils.keyboard.KeyboardUtil import com.google.android.material.bottomsheet.BottomSheetDialogFragment import kotlinx.coroutines.launch -class GameMessageInputDialog : +class GameMessageInputDialog(private var onSend: ((GameMessageInputDialog, String) -> Unit)?) : BottomSheetDialogFragment() { private var binding: GameMessageInputDialogBinding? = null @@ -34,9 +38,21 @@ class GameMessageInputDialog : binding?.inputEdit?.let { KeyboardUtil.showKeyboardInDialog(this.dialog, it) } - binding?.inputSend?.setOnClickListener { - SingleToastUtil.showToast("SEND") + binding?.inputEdit?.setOnKeyListener { _, keyCode, event -> + if (keyCode == KeyEvent.KEYCODE_ENTER && event.action == KeyEvent.ACTION_UP) { + send() + return@setOnKeyListener true + } + false } + binding?.inputSend?.setOnClickListener { + send() + } + } + + private fun send() { + val text = binding?.inputEdit?.text?.toString()?.trim() + onSend?.invoke(this, text ?: "") } private fun initObserver() { @@ -71,4 +87,9 @@ class GameMessageInputDialog : this.setCanceledOnTouchOutside(true) } } + + override fun onDestroy() { + super.onDestroy() + onSend = null + } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/bottom/GameBottomWidget.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/bottom/GameBottomWidget.kt index 70e72bdbf..5b92d13af 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/bottom/GameBottomWidget.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/bottom/GameBottomWidget.kt @@ -1,13 +1,17 @@ package com.chwl.app.game.ui.game.widgets.bottom import android.content.Context +import android.text.TextUtils import android.util.AttributeSet import android.view.LayoutInflater import androidx.databinding.DataBindingUtil import com.chwl.app.R import com.chwl.app.databinding.GameBottomWidgetBinding +import com.chwl.app.game.core.GameMessageAbility import com.chwl.app.game.ui.game.input.GameMessageInputDialog import com.chwl.core.support.room.FrameLayoutRoomWidget +import com.chwl.library.utils.ResUtil +import com.chwl.library.utils.SingleToastUtil class GameBottomWidget : FrameLayoutRoomWidget { @@ -36,8 +40,35 @@ class GameBottomWidget : FrameLayoutRoomWidget { init { binding.tvInput.setOnClickListener { roomView?.let { - GameMessageInputDialog().show(it.getViewFragmentManager(), "MESSAGE_INPUT") + roomContext?.let { context -> + GameMessageInputDialog { dialog, text -> + sendMessage(dialog, text) + }.show( + it.getViewFragmentManager(), + "MESSAGE_INPUT" + ) + } } } } + + private fun sendMessage(dialog: GameMessageInputDialog, message: String) { + if (TextUtils.isEmpty(message)) { + SingleToastUtil.showToast(ResUtil.getString(R.string.avroom_fragment_baseroomfragment_08)) + return + } + val messageAbility = + roomContext?.findAbility(GameMessageAbility::class.java.simpleName) + if (messageAbility == null) { + dialog.dismissAllowingStateLoss() + SingleToastUtil.showToast(ResUtil.getString(R.string.avroom_activity_roomblacklistactivity_015)) + return + } + addDisposable(messageAbility.sendTextMessage(message).subscribe({ + dialog.dismissAllowingStateLoss() + }, { + dialog.dismissAllowingStateLoss() + SingleToastUtil.showToast(it.message) + })) + } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageAdapter.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageAdapter.kt index b376a3a88..91d74f597 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageAdapter.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageAdapter.kt @@ -1,13 +1,54 @@ package com.chwl.app.game.ui.game.widgets.message +import android.content.Context +import android.graphics.Color +import android.graphics.drawable.Drawable +import android.text.TextUtils +import android.text.style.ForegroundColorSpan +import android.text.style.ImageSpan +import android.view.Gravity import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup +import android.widget.TextView +import androidx.core.content.ContextCompat +import androidx.core.content.res.ResourcesCompat import androidx.recyclerview.widget.RecyclerView -import androidx.recyclerview.widget.RecyclerView.ViewHolder import com.chwl.app.R +import com.chwl.app.common.widget.CustomImageSpan +import com.chwl.app.ui.utils.ImageLoadUtils +import com.chwl.app.ui.widget.TextSpannableBuilder +import com.chwl.core.auth.AuthModel +import com.chwl.core.level.UserLevelResourceType +import com.chwl.core.manager.AvRoomDataManager +import com.chwl.core.noble.NobleUtil +import com.chwl.core.user.bean.UserInfo +import com.chwl.library.common.util.Utils +import com.chwl.library.utils.ResUtil +import com.chwl.library.utils.SizeUtils +import com.example.lib_utils.AppUtils +import com.example.lib_utils.UiUtils.isRtl +import com.example.lib_utils.ktx.getColorById +import com.netease.nim.uikit.business.session.emoji.MoonUtil +import com.netease.nim.uikit.common.util.sys.ScreenUtil +import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage +import com.netease.nimlib.sdk.msg.constant.MsgTypeEnum + +class GameMessageAdapter(val dataList: MutableList) : + RecyclerView.Adapter(), View.OnClickListener { + + private val itemPaddingHorizontal = Utils.dip2px(AppUtils.getApp(), 11f) + private val itemPaddingVertical = Utils.dip2px(AppUtils.getApp(), 6f) + private val expLevelHeight = Utils.dip2px(AppUtils.getApp(), 18f) + private val badgeWidth = Utils.dip2px(AppUtils.getApp(), 15f) + private val badgeHeight = Utils.dip2px(AppUtils.getApp(), 15f) + private val greyColor = ContextCompat.getColor(AppUtils.getApp(), R.color.white_transparent_50) + private val imageSpanFactory = object : Function1 { + override fun invoke(p1: Drawable): ImageSpan { + return CustomImageSpan(p1) + } + } -class GameMessageAdapter(val dataList: MutableList) : - RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GameMessageNormalViewHolder { return GameMessageNormalViewHolder( LayoutInflater.from(parent.context) @@ -16,10 +57,151 @@ class GameMessageAdapter(val dataList: MutableList) : } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - val message = getItem(position) + val item = getItem(position) + val viewHolder = holder as GameMessageNormalViewHolder + viewHolder.textView.let { + it.setLineSpacing(0f, 1f) + it.setTextColor(Color.WHITE) + it.textSize = 12f + it.setOnClickListener(this) + it.setOnLongClickListener(null) + it.tag = item + it.gravity = Gravity.START or Gravity.CENTER_VERTICAL + if (isRtl(it.context)) { + it.textDirection = View.TEXT_DIRECTION_RTL + } + resetItemBackground(it) + } + if (item is ChatRoomMessage) { + if (item.msgType == MsgTypeEnum.text) { + convertText(viewHolder, item) + } + } } - fun getItem(position: Int): T? { + private fun convertText(holder: GameMessageNormalViewHolder, message: ChatRoomMessage) { + val textView = holder.textView + val text = TextSpannableBuilder(textView) + addUserTag(message, text, textView) + val nickName = + if (message.fromAccount != null && message.fromAccount == AuthModel.get().currentUid.toString() + "") { + ResUtil.getString(R.string.avroom_widget_messageview_0116) + } else { + NobleUtil.getNamePlate(UserInfo.NICK, message) + } + text.append(nickName, ForegroundColorSpan(greyColor)) + text.append( + ": " + message.content, + ForegroundColorSpan(textView.context.getColorById(R.color.white)) + ) + MoonUtil.replaceEmoticons( + textView.context, + text.builder.toString(), + text.builder, + imageSpanFactory + ) + textView.text = text.build() + loadItemBackgroundVip(message, textView) + } + + private fun addUserTag( + chatRoomMessage: ChatRoomMessage, + builder: TextSpannableBuilder, + textView: TextView + ) { + val extension = chatRoomMessage.chatRoomMessageExtension + val userLevel = NobleUtil.getLevel(UserLevelResourceType.EXPER_URL, chatRoomMessage) + val isOfficial = NobleUtil.getIsOfficial(UserInfo.IS_OFFICIAL, chatRoomMessage) + val vipIcon = NobleUtil.getResource(UserInfo.VIP_ICON, chatRoomMessage) + builder.append(vipIcon, expLevelHeight) + .append( + if (isOfficial) ResourcesCompat.getDrawable( + textView.resources, + R.mipmap.ic_user_official_13dp, null + ) else null, + badgeWidth, badgeHeight + ) + .append(getNewUserDrawable(textView.context, chatRoomMessage), badgeWidth, badgeHeight) + .append( + if (AvRoomDataManager.get() + .isSuperAdmin(chatRoomMessage.fromAccount) + ) ResourcesCompat.getDrawable( + textView.resources, + R.drawable.ic_room_super_admin, null + ) else null, + SizeUtils.dp2px(textView.context, 23f), expLevelHeight + ) + + // 官方主播認證 + val tvOfficialMask = + NobleUtil.getLevel(UserInfo.OAC_NAME, chatRoomMessage).trim { it <= ' ' } + val ivOfficialMask = NobleUtil.getLevel(UserInfo.OAC_ICON, chatRoomMessage) + if (!TextUtils.isEmpty(tvOfficialMask) && !TextUtils.isEmpty(ivOfficialMask) && extension != null) { // extension != null 表示自己 + builder.appendBgAndContent(ivOfficialMask, tvOfficialMask) + } else if (!TextUtils.isEmpty(ivOfficialMask)) { + builder.append(ivOfficialMask, SizeUtils.dp2px(textView.context, 62f), expLevelHeight) + } + //等級 + builder.append(userLevel, expLevelHeight) + //銘牌 + val tvNamePlate = + NobleUtil.getNamePlate(UserInfo.NAMEPLATE_WORD, chatRoomMessage).trim { it <= ' ' } + val ivNamePlate = NobleUtil.getNamePlate(UserInfo.NAMEPLATE_PIC, chatRoomMessage) + if (!TextUtils.isEmpty(tvNamePlate) && !TextUtils.isEmpty(ivNamePlate)) { // extension != null 表示自己 + builder.appendBgAndContent(ivNamePlate, tvNamePlate) + } else if (!TextUtils.isEmpty(ivNamePlate)) { + builder.append(ivNamePlate, expLevelHeight) + } + } + + private fun getNewUserDrawable(context: Context, chatRoomMessage: ChatRoomMessage): Drawable? { + val newUser = NobleUtil.getIsNewUser(UserInfo.IS_NEW_USER, chatRoomMessage) + val isHelloUser = + NobleUtil.getIsNewUser(UserInfo.IS_FROM_SAY_HELLO_CHANNEL, chatRoomMessage) + return if (newUser) { + ResourcesCompat.getDrawable( + context.resources, + if (isHelloUser) R.drawable.ic_new_user_hello else R.drawable.ic_new_user, + null + ) + } else null + } + + private fun resetItemBackground(textView: TextView) { + textView.text = "" + textView.setBackgroundResource(R.drawable.shape_room_message_bg) + textView.setPadding( + itemPaddingHorizontal, + itemPaddingVertical, + itemPaddingHorizontal, + itemPaddingVertical + ) + } + + private fun loadItemBackgroundVip(chatRoomMessage: ChatRoomMessage?, view: View) { + val androidBubbleUrl = NobleUtil.getResource(UserInfo.BUBBLE_URL_ANDROID, chatRoomMessage) + if (TextUtils.isEmpty(androidBubbleUrl)) return + view.setPadding( + itemPaddingHorizontal, + ScreenUtil.dip2px(10f), + itemPaddingHorizontal, + ScreenUtil.dip2px(10f) + ) + ImageLoadUtils.loadNinePatchBg(view, androidBubbleUrl) + } + + fun setDataList(list: List) { + dataList.clear() + dataList.addAll(list) + notifyDataSetChanged() + } + + fun addData(item: Any) { + dataList.add(item) + notifyItemInserted(dataList.size) + } + + fun getItem(position: Int): Any? { return dataList.get(position) } @@ -27,8 +209,7 @@ class GameMessageAdapter(val dataList: MutableList) : return dataList.size } - protected open fun convert(holder: GameMessageNormalViewHolder, item: T){ + override fun onClick(v: View) { } - } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageNormalViewHolder.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageNormalViewHolder.kt index 83478df3e..af738568d 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageNormalViewHolder.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageNormalViewHolder.kt @@ -7,10 +7,6 @@ import com.chwl.app.R class GameMessageNormalViewHolder(val itemView: View) : RecyclerView.ViewHolder(itemView) { - var textView: TextView? = null - - init { - textView = itemView.findViewById(R.id.tv_content) - } + var textView: TextView = itemView.findViewById(R.id.tv_content) } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageWidget.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageWidget.kt index 845f0ee2a..6bed89d03 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageWidget.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/widgets/message/GameMessageWidget.kt @@ -6,16 +6,19 @@ import android.view.ViewGroup import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.chwl.app.R +import com.chwl.app.game.core.GameMessageAbility import com.chwl.app.ui.widget.DividerItemDecoration import com.chwl.app.ui.widget.MyItemAnimator import com.chwl.app.ui.widget.RecyclerViewNoViewpagerScroll import com.chwl.core.support.room.FrameLayoutRoomWidget -import com.netease.nimlib.sdk.chatroom.model.ChatRoomMessage +import com.chwl.core.support.room.RoomContext +import com.chwl.core.support.room.RoomView -class GameMessageWidget : FrameLayoutRoomWidget { +class GameMessageWidget : FrameLayoutRoomWidget, GameMessageAbility.Listener { private val recyclerView = RecyclerViewNoViewpagerScroll(context) - private val adapter = GameMessageAdapter(ArrayList()) + private val adapter = GameMessageAdapter(ArrayList()) + private var isAtBottom = true constructor(context: Context) : super(context) constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) @@ -34,6 +37,7 @@ class GameMessageWidget : FrameLayoutRoomWidget { init { initListView() + initEvent() } private fun initListView() { @@ -63,4 +67,55 @@ class GameMessageWidget : FrameLayoutRoomWidget { } recyclerView.adapter = adapter } + + private fun initEvent() { + val layoutManager = recyclerView.layoutManager as LinearLayoutManager + recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + val lastPosition = layoutManager.findLastVisibleItemPosition() + if (lastPosition == RecyclerView.NO_POSITION) { + isAtBottom = true + } else isAtBottom = lastPosition == adapter.itemCount - 1 + } + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + if (dy < 0) { + isAtBottom = false + } + } + }) + } + + override fun onInitialize(roomView: RoomView, roomContext: RoomContext) { + super.onInitialize(roomView, roomContext) + val messageAbility = + roomContext.findAbility(GameMessageAbility::class.java.simpleName) + messageAbility?.getMessageList()?.let { + adapter.setDataList(it) + } + messageAbility?.addListener(this) + } + + override fun onAddMessage(message: Any) { + adapter.addData(message) + tryScrollToBottom() + } + + override fun onReplaceMessageList(list: List) { + adapter.setDataList(list) + tryScrollToBottom() + } + + private fun tryScrollToBottom() { + logD("tryScrollToBottom isAtBottom:$isAtBottom") + if (isAtBottom) { + if (adapter.itemCount > 0) { + recyclerView.smoothScrollToPosition(adapter.itemCount - 1) + } + } + } } \ No newline at end of file diff --git a/app/src/module_game/res/layout/game_activity.xml b/app/src/module_game/res/layout/game_activity.xml index 713e08b89..e8af7d803 100644 --- a/app/src/module_game/res/layout/game_activity.xml +++ b/app/src/module_game/res/layout/game_activity.xml @@ -103,10 +103,13 @@ + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintHorizontal_bias="0.277" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintWidth_percent="0.75" /> (StatusCode.INVALID) + var messageFilter: ChatRoomMessageFilter? = null + private val receiveMessageObserver = Observer> { + logD("receiveMessageObserver size:${it.size}") val list = it.filter { item -> - item.sessionId == sessionId && !ChatRoomClientManager.publicChatRoomReceiveMessageFilter.filter( - item - ) + item.sessionId == sessionId && (messageFilter?.filter(item) ?: true) } messagePublishSubject.onNext(list) } private val onlineStatusObserver = Observer { + if (it.roomId != sessionId) { + return@Observer + } if (it.status == StatusCode.LOGINED) { isLogin = true } else { diff --git a/app/src/module_public_chat/java/com/chwl/app/public_chat/core/ChatRoomClientManager.kt b/app/src/module_public_chat/java/com/chwl/app/public_chat/core/ChatRoomClientManager.kt index ede47347f..324b5ced8 100644 --- a/app/src/module_public_chat/java/com/chwl/app/public_chat/core/ChatRoomClientManager.kt +++ b/app/src/module_public_chat/java/com/chwl/app/public_chat/core/ChatRoomClientManager.kt @@ -13,7 +13,7 @@ object ChatRoomClientManager { val publicChatRoomReceiveMessageFilter: ChatRoomMessageFilter = object : ChatRoomMessageFilter { override fun filter(message: ChatRoomMessage): Boolean { if (message.msgType == MsgTypeEnum.image || message.msgType == MsgTypeEnum.text) { - return false + return true } if (message.msgType == MsgTypeEnum.custom) { val attachment: CustomAttachment = @@ -22,19 +22,24 @@ object ChatRoomClientManager { CustomAttachment.CUSTOM_MSG_HEADLINE_CHANGED -> { when (attachment.second) { CustomAttachment.CUSTOM_MSG_HEADLINE_CHANGED_SUB -> { - return false + return true } } } } } - return true + return false } } fun getPublicChatClient(): ChatRoomClient? { val sessionId = InitialModel.get().publicChatSessionId - return getClient(sessionId) + if (sessionId.isNullOrEmpty()) { + return null + } + return getClient(sessionId).apply { + this.messageFilter = publicChatRoomReceiveMessageFilter + } } fun getClient(sessionId: String): ChatRoomClient { diff --git a/app/src/module_public_chat/java/com/chwl/app/public_chat/ui/message/PublicChatRoomViewModel.kt b/app/src/module_public_chat/java/com/chwl/app/public_chat/ui/message/PublicChatRoomViewModel.kt index 0a70ba091..7a11972ad 100644 --- a/app/src/module_public_chat/java/com/chwl/app/public_chat/ui/message/PublicChatRoomViewModel.kt +++ b/app/src/module_public_chat/java/com/chwl/app/public_chat/ui/message/PublicChatRoomViewModel.kt @@ -20,6 +20,7 @@ class PublicChatRoomViewModel : BaseViewModel() { fun init(sessionId: String) { val client = ChatRoomClientManager.getClient(sessionId) + client.messageFilter = ChatRoomClientManager.publicChatRoomReceiveMessageFilter chatRoomClient = client registerReceiveMessage(client) } diff --git a/core/src/main/java/com/chwl/core/support/room/FrameLayoutRoomWidget.kt b/core/src/main/java/com/chwl/core/support/room/FrameLayoutRoomWidget.kt index f13f7839e..92df1c298 100644 --- a/core/src/main/java/com/chwl/core/support/room/FrameLayoutRoomWidget.kt +++ b/core/src/main/java/com/chwl/core/support/room/FrameLayoutRoomWidget.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.Observer import com.chwl.core.utils.extension.toast import com.chwl.library.net.rxnet.exception.ExceptionHandle import io.reactivex.disposables.CompositeDisposable +import io.reactivex.disposables.Disposable import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.MainScope import kotlinx.coroutines.cancel @@ -110,6 +111,10 @@ abstract class FrameLayoutRoomWidget : FrameLayout, RoomWidget { return disposable } + protected fun addDisposable(disposable: Disposable) { + getCompositeDisposable().add(disposable) + } + protected fun safeLaunch( onError: suspend(e: Throwable) -> Unit = { if (it.message != "Job was cancelled") {