diff --git a/.gitignore b/.gitignore index d7496dd1d..fb14191c0 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,6 @@ .externalNativeBuild .cxx /app/normal/* -/app/src/normal/java/io/agora/education/fetchtoken/ \ No newline at end of file +/app/src/normal/java/io/agora/education/fetchtoken/ +app/release/app-release.apk +app/release/output-metadata.json diff --git a/AgoraClassSDK/build.gradle b/AgoraClassSDK/build.gradle index 1cb4ae239..8d43c97e8 100644 --- a/AgoraClassSDK/build.gradle +++ b/AgoraClassSDK/build.gradle @@ -11,8 +11,6 @@ android { defaultConfig { minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion - versionCode rootProject.ext.ClassSDK.versionCode - versionName rootProject.ext.ClassSDK.versionName consumerProguardFiles "consumer-rules.pro" @@ -35,6 +33,7 @@ android { viewBinding { enabled = true } + namespace 'io.agora.agoraclasssdk' } dependencies { @@ -47,9 +46,9 @@ dependencies { compileOnly project(path: ':AgoraEduUIKit') if (readyPublishGithub.toBoolean()) { - compileOnly "io.github.agoraio-community:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" + compileOnly "io.github.agora-apaas:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" } else { - compileOnly project(path: ':AgoraEduCore') + implementation project(path: ':AgoraEduCore') } } diff --git a/AgoraClassSDK/src/main/AndroidManifest.xml b/AgoraClassSDK/src/main/AndroidManifest.xml index 160fcf982..36ca8b73a 100644 --- a/AgoraClassSDK/src/main/AndroidManifest.xml +++ b/AgoraClassSDK/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/AgoraCloudScene/build.gradle b/AgoraCloudScene/build.gradle index 322b83fc2..7766851f4 100644 --- a/AgoraCloudScene/build.gradle +++ b/AgoraCloudScene/build.gradle @@ -17,8 +17,6 @@ android { defaultConfig { minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion - versionCode rootProject.ext.AgoraCloudScene.versionCode - versionName rootProject.ext.AgoraCloudScene.versionName buildConfigField 'String', 'AgoraCloudScene', String.format("\"%s\"", rootProject.ext.AgoraCloudScene.versionName) consumerProguardFiles "consumer-rules.pro" @@ -47,6 +45,7 @@ android { res.srcDirs = ['src/main/res'] } } + namespace 'io.agora.online' } dependencies { @@ -64,7 +63,7 @@ dependencies { implementation "androidx.activity:activity-ktx:1.2.2" // for ide hint if (readyPublishGithub.toBoolean()) { - compileOnly "io.github.agoraio-community:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" + compileOnly "io.github.agora-apaas:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" } else { implementation project(path: ':AgoraEduCore') } diff --git a/AgoraCloudScene/src/main/AndroidManifest.xml b/AgoraCloudScene/src/main/AndroidManifest.xml index 5ae47eff6..dcdb7b580 100644 --- a/AgoraCloudScene/src/main/AndroidManifest.xml +++ b/AgoraCloudScene/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/AgoraEduUIKit/build.gradle b/AgoraEduUIKit/build.gradle index a45a01998..aa3a51d34 100644 --- a/AgoraEduUIKit/build.gradle +++ b/AgoraEduUIKit/build.gradle @@ -15,8 +15,6 @@ android { defaultConfig { minSdkVersion rootProject.ext.android.minSdkVersion targetSdkVersion rootProject.ext.android.targetSdkVersion - versionCode rootProject.ext.EduUIKit.versionCode - versionName rootProject.ext.EduUIKit.versionName buildConfigField 'String', 'AgoraEduUIKitVersion', String.format("\"%s\"", rootProject.ext.EduUIKit.versionName) consumerProguardFiles "consumer-rules.pro" @@ -45,6 +43,7 @@ android { res.srcDirs = ['src/main/res'] } } + namespace 'io.agora.agoraeduuikit' } dependencies { @@ -62,9 +61,9 @@ dependencies { implementation "androidx.activity:activity-ktx:1.2.2" // for ide hint if (readyPublishGithub.toBoolean()) { - compileOnly "io.github.agoraio-community:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" + compileOnly "io.github.agora-apaas:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" } else { - compileOnly project(path: ':AgoraEduCore') + implementation project(path: ':AgoraEduCore') } // for agora chat diff --git a/AgoraEduUIKit/src/main/AndroidManifest.xml b/AgoraEduUIKit/src/main/AndroidManifest.xml index 38c8eb29b..fe3ac2707 100644 --- a/AgoraEduUIKit/src/main/AndroidManifest.xml +++ b/AgoraEduUIKit/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + diff --git a/AgoraEduUIKit/src/main/java/com/agora/edu/component/chat/AgoraEduEaseChatWidget.kt b/AgoraEduUIKit/src/main/java/com/agora/edu/component/chat/AgoraEduEaseChatWidget.kt index 20884215f..0631c5418 100644 --- a/AgoraEduUIKit/src/main/java/com/agora/edu/component/chat/AgoraEduEaseChatWidget.kt +++ b/AgoraEduUIKit/src/main/java/com/agora/edu/component/chat/AgoraEduEaseChatWidget.kt @@ -47,6 +47,9 @@ class AgoraEduEaseChatWidget : ChatPopupWidget(), InputMsgListener, ChatPagerLis private var appKey = "" private var role = EaseConstant.ROLE_STUDENT private var easeChatId = "" // 环信ID + private var _recvRoomIds:List<*>? = null + private var _sendRoomIds:List<*>? = null + private var _chatGroupUuids:List<*>? = null private var userUuid = "" private var mChatRoomId = "" private var nickName = "" @@ -55,6 +58,7 @@ class AgoraEduEaseChatWidget : ChatPopupWidget(), InputMsgListener, ChatPagerLis private var chatViewPager: ChatViewPager? = null private var contentLayout: FrameLayout? = null private var inputView: InputView? = null + private var _chatGroupMap: MutableMap<*, *>? = null // specified input`s parentView private var specialInputViewParent: ViewGroup? = null @@ -69,6 +73,47 @@ class AgoraEduEaseChatWidget : ChatPopupWidget(), InputMsgListener, ChatPagerLis var isNeedRoomMutedStatus = true //是否需要判断禁言状态 + + val sendRoomIds: List + get() { + val list = mutableListOf() + _sendRoomIds?.forEach { + list.add(it.toString()) + } + return list + } + + val recvRoomIds: List + get() { + val list = mutableListOf() + _recvRoomIds?.forEach { + list.add(it.toString()) + } + return list + } + + val chatGroupUuids: List + get() { + val list = mutableListOf() + _chatGroupUuids?.forEach { + list.add(it.toString()) + } + return list + } + + val userRoomIds: List + get() { + val list = mutableListOf() + _chatGroupUuids?.forEach { + val groupUuid = it.toString() + val chatRoomId = _chatGroupMap?.get(groupUuid).toString() + list.add(chatRoomId) + } + return list + } + + + override fun init(container: ViewGroup) { super.init(container) mContext = container.context @@ -109,6 +154,10 @@ class AgoraEduEaseChatWidget : ChatPopupWidget(), InputMsgListener, ChatPagerLis it.setCloseable(false) it.setAvatarUrl(avatarUrl) it.setChatRoomId(mChatRoomId) + it.setRecvRoomIds(recvRoomIds) + it.setSendRoomIds(sendRoomIds) + it.setUserRoomIds(userRoomIds) + it.setChatGroupUuids(chatGroupUuids) it.setNickName(nickName) it.setRoomUuid(roomUuid) it.setUserName(easeChatId) @@ -184,6 +233,7 @@ class AgoraEduEaseChatWidget : ChatPopupWidget(), InputMsgListener, ChatPagerLis } } + private fun parseEaseConfigProperties(): Boolean { // val properties = eduContext?.widgetContext2()?.getWidgetProperties(WidgetType.IM) val extraProperties = this.widgetInfo?.roomProperties as? MutableMap<*, *> @@ -192,12 +242,17 @@ class AgoraEduEaseChatWidget : ChatPopupWidget(), InputMsgListener, ChatPagerLis appName = it["appName"] as? String ?: "" mChatRoomId = it["chatRoomId"] as? String ?: mChatRoomId appKey = it["appKey"] as? String ?: "" + val chatGroup = it["chatGroup"] as? MutableMap<*, *> + _chatGroupMap = chatGroup?.get("groups") as? MutableMap<*, *> role = this.widgetInfo?.localUserInfo?.userRole ?: EaseConstant.ROLE_STUDENT EaseRepository.instance.role = role } this.widgetInfo?.localUserProperties?.let { easeChatId = it[userIdKey].toString() + _sendRoomIds = it[sendRoomIdKey] as? List<*> + _recvRoomIds = it[recvRoomIdKey] as? List<*> + _chatGroupUuids = it[chatGroupUuidsKey] as? List<*> } return !TextUtils.isEmpty(easeChatId) && !TextUtils.isEmpty(orgName) diff --git a/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/constant/EaseConstant.kt b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/constant/EaseConstant.kt index d0da0a63d..12afa2f4e 100644 --- a/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/constant/EaseConstant.kt +++ b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/constant/EaseConstant.kt @@ -6,6 +6,7 @@ object EaseConstant { const val NORMAL_MSG = 0 const val ROLE = "role" + const val CHAT_GROUP_UUIDS = "chatGroupUuids" const val ROLE_TEACHER = 1 const val ROLE_STUDENT = 2 const val ROLE_ASSISTANT = 3 diff --git a/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/exception/ChatError.kt b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/exception/ChatError.kt new file mode 100644 index 000000000..5ff37fc1e --- /dev/null +++ b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/exception/ChatError.kt @@ -0,0 +1,5 @@ +package com.hyphenate.easeim.modules.exception + +class ChatError(_code:Int, message: String) : RuntimeException(message) { + val code = _code +} \ No newline at end of file diff --git a/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/repositories/EaseRepository.kt b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/repositories/EaseRepository.kt index 90d87acf9..1ba92facd 100644 --- a/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/repositories/EaseRepository.kt +++ b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/repositories/EaseRepository.kt @@ -1,12 +1,20 @@ package com.hyphenate.easeim.modules.repositories import com.hyphenate.easeim.modules.constant.EaseConstant +import com.hyphenate.easeim.modules.exception.ChatError import com.hyphenate.easeim.modules.manager.ThreadManager import com.hyphenate.easeim.modules.view.`interface`.EaseOperationListener import io.agora.CallBack +import io.agora.Error import io.agora.ValueCallBack import io.agora.chat.* import io.agora.util.EMLog +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine class EaseRepository { companion object { @@ -32,80 +40,107 @@ class EaseRepository { var roomUuid = "" var userName = "" var userUuid = "" - + var recvRoomIds: List = emptyList() /** * 加载本地消息 */ fun loadMessages() { if (isInit) { - val conversation = ChatClient.getInstance().chatManager() - .getConversation(chatRoomId, Conversation.ConversationType.ChatRoom, true) - val msgList = conversation?.allMessages val norMsgList = mutableListOf() - msgList?.forEach { message -> - val msgType = message.getIntAttribute(EaseConstant.MSG_TYPE, EaseConstant.NORMAL_MSG) - if (msgType == EaseConstant.NORMAL_MSG) - norMsgList.add(message) + recvRoomIds.forEach { roomId-> + val conversation = ChatClient.getInstance().chatManager() + .getConversation(roomId, Conversation.ConversationType.ChatRoom, true) + val msgList = conversation?.allMessages + msgList?.forEach { message -> + val msgType = message.getIntAttribute(EaseConstant.MSG_TYPE, EaseConstant.NORMAL_MSG) + if (msgType == EaseConstant.NORMAL_MSG) + norMsgList.add(message) + } } + norMsgList.sortBy { msg-> msg.msgTime} for (listener in listeners) { listener.loadMessageFinish(norMsgList) } } } + + + + suspend fun getHistoryMsgs(roomId:String):List = suspendCoroutine { cont -> + ChatClient.getInstance().chatManager().asyncFetchHistoryMessage(roomId, Conversation.ConversationType.ChatRoom, 50, "", object : ValueCallBack> { + override fun onSuccess(value: CursorResult?) { + cont.resume(value?.data ?: emptyList()) + } + + override fun onError(error: Int, errorMsg: String?) { + val message = "loadHistoryMessages failed: $error = $errorMsg" + cont.resumeWithException(ChatError(error, message)) + EMLog.e(TAG, message) + } + + }) + } + + private suspend fun batchGetHistoryMsgs(roomIds:List):List { + val list = mutableListOf() + roomIds.forEach { i-> + val item = getHistoryMsgs(i) + item.forEach { j-> + list.add(j) + } + } + return list + } + /** * 漫游50条历史消息 */ fun loadHistoryMessages() { EMLog.e(TAG, "loadHistoryMessages") - ChatClient.getInstance().chatManager().asyncFetchHistoryMessage(chatRoomId, Conversation.ConversationType.ChatRoom, 50, "", object : ValueCallBack> { - override fun onSuccess(value: CursorResult?) { - value?.data?.forEach { message -> - if (message.type == ChatMessage.Type.CMD) { - val body = message.body as CmdMessageBody - val notifyMessage = ChatMessage.createSendMessage(ChatMessage.Type.CUSTOM) - val notifyBody = CustomMessageBody(EaseConstant.NOTIFY) - when (body.action()) { - EaseConstant.SET_ALL_MUTE, EaseConstant.REMOVE_ALL_MUTE -> { - notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) - } - EaseConstant.DEL -> { - val msgId = message.getStringAttribute(EaseConstant.MSG_ID, "") - deleteMessage(chatRoomId, msgId) - notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) - } - EaseConstant.MUTE, EaseConstant.UN_MUTE -> { - val member = message.getStringAttribute(EaseConstant.MUTE_MEMEBER, "") - if (!member.equals(ChatClient.getInstance().currentUser)) - return@forEach - notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) - } + CoroutineScope(Dispatchers.Main).launch { + val msgs = batchGetHistoryMsgs(recvRoomIds) + msgs.forEach { message-> + if (message.type == ChatMessage.Type.CMD) { + val body = message.body as CmdMessageBody + val notifyMessage = ChatMessage.createSendMessage(ChatMessage.Type.CUSTOM) + val notifyBody = CustomMessageBody(EaseConstant.NOTIFY) + when (body.action()) { + EaseConstant.SET_ALL_MUTE, EaseConstant.REMOVE_ALL_MUTE -> { + notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) + + } + EaseConstant.DEL -> { + val msgId = message.getStringAttribute(EaseConstant.MSG_ID, "") + deleteMessage(chatRoomId, msgId) + notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) + } + EaseConstant.MUTE, EaseConstant.UN_MUTE -> { + val member = message.getStringAttribute(EaseConstant.MUTE_MEMEBER, "") + if (!member.equals(ChatClient.getInstance().currentUser)) + return@forEach + notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) } - notifyMessage.body = notifyBody - notifyMessage.to = chatRoomId - notifyMessage.chatType = ChatMessage.ChatType.ChatRoom - notifyMessage.setStatus(ChatMessage.Status.SUCCESS) - notifyMessage.msgTime = message.msgTime - notifyMessage.msgId = message.msgId - notifyMessage.setAttribute(EaseConstant.NICK_NAME, message.getStringAttribute(EaseConstant.NICK_NAME, message.from)) - ChatClient.getInstance().chatManager().saveMessage(notifyMessage) - } - } - ThreadManager.instance.runOnMainThread { - val conversation = ChatClient.getInstance().chatManager().getConversation(chatRoomId, Conversation.ConversationType.ChatRoom, true) - conversation.loadMoreMsgFromDB("", 50) - for (listener in listeners) { - listener.loadHistoryMessageFinish() } + notifyMessage.body = notifyBody + notifyMessage.to = chatRoomId + notifyMessage.chatType = ChatMessage.ChatType.ChatRoom + notifyMessage.setStatus(ChatMessage.Status.SUCCESS) + notifyMessage.msgTime = message.msgTime + notifyMessage.msgId = message.msgId + notifyMessage.setAttribute(EaseConstant.NICK_NAME, message.getStringAttribute(EaseConstant.NICK_NAME, message.from)) + ChatClient.getInstance().chatManager().saveMessage(notifyMessage) } } - - override fun onError(error: Int, errorMsg: String?) { - EMLog.e(TAG, "loadHistoryMessages failed: $error = $errorMsg") + ThreadManager.instance.runOnMainThread { + val conversation = ChatClient.getInstance().chatManager().getConversation(chatRoomId, Conversation.ConversationType.ChatRoom, true) + conversation.loadMoreMsgFromDB("", 50) + for (listener in listeners) { + listener.loadHistoryMessageFinish() + } } - - }) + } } fun refreshLastMessageId() { diff --git a/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/view/ui/widget/ChatViewPager.kt b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/view/ui/widget/ChatViewPager.kt index 6abf8db96..63d5fa6cd 100644 --- a/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/view/ui/widget/ChatViewPager.kt +++ b/AgoraEduUIKit/src/main/java/com/hyphenate/easeim/modules/view/ui/widget/ChatViewPager.kt @@ -17,27 +17,33 @@ import androidx.annotation.UiThread import androidx.core.content.ContextCompat import androidx.viewpager.widget.ViewPager import com.google.android.material.tabs.TabLayout -import io.agora.agoraeduuikit.R import com.hyphenate.easeim.modules.constant.EaseConstant +import com.hyphenate.easeim.modules.exception.ChatError import com.hyphenate.easeim.modules.manager.ThreadManager import com.hyphenate.easeim.modules.repositories.EaseRepository import com.hyphenate.easeim.modules.utils.CommonUtil import com.hyphenate.easeim.modules.utils.ScreenUtil +import com.hyphenate.easeim.modules.view.adapter.ChatViewPagerAdapter import com.hyphenate.easeim.modules.view.`interface`.ChatPagerListener import com.hyphenate.easeim.modules.view.`interface`.ViewEventListener -import com.hyphenate.easeim.modules.view.adapter.ChatViewPagerAdapter import io.agora.* +import io.agora.agoraeduuikit.R import io.agora.chat.* import io.agora.util.EMLog import io.agora.util.FileHelper import io.agora.util.VersionUtils -import io.agora.Error +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import okhttp3.* import org.json.JSONException import org.json.JSONObject import java.io.File import java.io.IOException import java.util.concurrent.Executors +import kotlin.coroutines.resume +import kotlin.coroutines.resumeWithException +import kotlin.coroutines.suspendCoroutine class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) : LinearLayout(context, attributeSet, defStyleAttr), MessageListener, ChatRoomChangeListener, ViewEventListener, ConnectionListener { @@ -47,7 +53,10 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: companion object { private const val TAG = "ChatViewPager" } - + private var userRoomIds: List = emptyList() + private var sendRoomIds: List = emptyList() + private var recvRoomIds: List = emptyList() + private var chatGroupUuids: List = emptyList() private lateinit var viewPager: ViewPager private lateinit var tabLayout: TabLayout private lateinit var iconHidden: ImageView @@ -79,6 +88,23 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: private val selectImageResultCode = 78 private var executor = Executors.newScheduledThreadPool(1) + val joinRoomIds:List + get() { + val list = mutableListOf() + list.add(chatRoomId) + sendRoomIds.forEach { + if(list.indexOf(it) == -1){ + list.add(it) + } + } + recvRoomIds.forEach { + if(list.indexOf(it) == -1){ + list.add(it) + } + } + return list + } + init { LayoutInflater.from(context).inflate(R.layout.fcr_chat_total_layout, this) container = findViewById(R.id.total_layout) @@ -195,13 +221,19 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: override fun onMessageReceived(messages: MutableList?) { messages?.let { for (message in messages) { - if (message.getIntAttribute(EaseConstant.MSG_TYPE, EaseConstant.NORMAL_MSG) == EaseConstant.NORMAL_MSG) { - ThreadManager.instance.runOnMainThread { - if (chooseTab != chatTab) { - showUnread(chatTab) + if (message.chatType == ChatMessage.ChatType.ChatRoom && recvRoomIds.indexOf(message.to) != -1) { + if (message.getIntAttribute( + EaseConstant.MSG_TYPE, + EaseConstant.NORMAL_MSG + ) == EaseConstant.NORMAL_MSG + ) { + ThreadManager.instance.runOnMainThread { + if (chooseTab != chatTab) { + showUnread(chatTab) + } + showOuterLayerUnread() + refreshUI() } - showOuterLayerUnread() - refreshUI() } } } @@ -212,17 +244,34 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: override fun onCmdMessageReceived(messages: MutableList?) { messages?.forEach { message -> - if (message.chatType == ChatMessage.ChatType.ChatRoom && message.to == chatRoomId) { + if (message.chatType == ChatMessage.ChatType.ChatRoom && recvRoomIds.indexOf(message.to) != -1) { val body = message.body as CmdMessageBody val notifyMessage = ChatMessage.createSendMessage(ChatMessage.Type.CUSTOM) val notifyBody = CustomMessageBody(EaseConstant.NOTIFY) when (body.action()) { EaseConstant.SET_ALL_MUTE, EaseConstant.REMOVE_ALL_MUTE -> { notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) + + val isMuted = body.action() == EaseConstant.SET_ALL_MUTE + EaseRepository.instance.allMuted = isMuted + ThreadManager.instance.runOnMainThread { + if (isMuted) { + chatView.showMutedView() + chatPagerListener?.onMuted(isMuted) + } else { + if (EaseRepository.instance.singleMuted) { + chatView.showMutedView() + } else { + chatView.hideMutedView() + chatPagerListener?.onMuted(isMuted) + } + } + } } EaseConstant.DEL -> { val msgId = message.getStringAttribute(EaseConstant.MSG_ID, "") - EaseRepository.instance.deleteMessage(chatRoomId, msgId) + + EaseRepository.instance.deleteMessage(message.to, msgId) notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) } EaseConstant.MUTE, EaseConstant.UN_MUTE -> { @@ -230,10 +279,21 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: if (!member.equals(userName)) return@forEach notifyBody.params = mutableMapOf(Pair(EaseConstant.OPERATION, body.action())) + + val isMuted = body.action() == EaseConstant.MUTE + EaseRepository.instance.singleMuted = isMuted + ThreadManager.instance.runOnMainThread { + if(isMuted){ + chatView.showMutedView() + }else{ + chatView.hideMutedView() + } + chatPagerListener?.onMuted(isMuted) + } } } notifyMessage.body = notifyBody - notifyMessage.to = chatRoomId + notifyMessage.to = message.to notifyMessage.chatType = ChatMessage.ChatType.ChatRoom notifyMessage.setStatus(ChatMessage.Status.SUCCESS) notifyMessage.msgTime = message.msgTime @@ -289,28 +349,11 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: mutes: MutableList?, expireTime: Long ) { - mutes?.forEach { - if (it == userName) { - EaseRepository.instance.singleMuted = true - ThreadManager.instance.runOnMainThread { - chatView.showMutedView() - chatPagerListener?.onMuted(true) - } - } - } } override fun onMuteListRemoved(chatRoomId: String?, mutes: MutableList?) { mutes?.forEach { - if (it == userName) { - EaseRepository.instance.singleMuted = false - ThreadManager.instance.runOnMainThread { - if (!EaseRepository.instance.allMuted) { - chatView.hideMutedView() - chatPagerListener?.onMuted(false) - } - } - } + } } @@ -323,20 +366,7 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: } override fun onAllMemberMuteStateChanged(chatRoomId: String?, isMuted: Boolean) { - EaseRepository.instance.allMuted = isMuted - ThreadManager.instance.runOnMainThread { - if (isMuted) { - chatView.showMutedView() - chatPagerListener?.onMuted(isMuted) - } else { - if (EaseRepository.instance.singleMuted) { - chatView.showMutedView() - } else { - chatView.hideMutedView() - chatPagerListener?.onMuted(isMuted) - } - } - } + } override fun onAdminAdded(chatRoomId: String?, admin: String?) { @@ -490,6 +520,7 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: info.avatarUrl = avatarUrl val extJson = JSONObject() extJson.put(EaseConstant.ROLE, EaseConstant.ROLE_STUDENT) + extJson.put(EaseConstant.CHAT_GROUP_UUIDS, chatGroupUuids) info.ext = extJson.toString() EaseRepository.instance.updateOwnInfo(info) joinChatRoom() @@ -512,40 +543,53 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: }) } + + suspend fun joinRoom(roomId:String):ChatRoom = suspendCoroutine { cont -> + ChatClient.getInstance().chatroomManager().joinChatRoom(roomId, object : ValueCallBack { + override fun onSuccess(value: ChatRoom?) { + cont.resume(value!!) + + } + + override fun onError(error: Int, errorMsg: String) { + cont.resumeWithException(ChatError(error, errorMsg)) + } + }) + } + + + suspend fun batchJoinRoom(roomIds: List):List { + val list = mutableListOf() + roomIds.forEach { + val room = joinRoom(it) + list.add(room) + } + return list + } + /** * 加入聊天室 */ private fun joinChatRoom() { - joinLimit++ - ChatClient.getInstance().chatroomManager().joinChatRoom(chatRoomId, object : ValueCallBack { - override fun onSuccess(value: ChatRoom?) { + CoroutineScope(Dispatchers.Main).launch { + try { + batchJoinRoom(joinRoomIds) EMLog.e("Login:", "join success") ThreadManager.instance.runOnMainThread { EaseRepository.instance.isLogin = true initIMListener() chatView.initData() } - } - - override fun onError(error: Int, errorMsg: String) { - EMLog.e(TAG, "join failed: $error:$errorMsg") - if (error == Error.CHATROOM_ALREADY_JOINED) { + }catch (e:ChatError){ + if (e.code == Error.CHATROOM_ALREADY_JOINED) { ThreadManager.instance.runOnMainThread { EaseRepository.instance.isLogin = true initIMListener() chatView.initData() } - return - } - if (joinLimit == 2) { - ThreadManager.instance.runOnMainThread { - Toast.makeText(context, context.getString(R.string.fcr_hyphenate_im_login_chat_failed)+"--"+context.getString(R.string.fcr_hyphenate_im_join_chat_room_failed)+":$error:$errorMsg", Toast.LENGTH_SHORT).show() - } - return } - joinChatRoom() } - }) + } } override fun onAnnouncementClick() { @@ -590,19 +634,20 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: override fun onConnected() { EMLog.e(TAG, "onConnected") if(EaseRepository.instance.isInit && EaseRepository.instance.isLogin){ - ChatClient.getInstance().chatroomManager().joinChatRoom(chatRoomId, object : ValueCallBack { - override fun onSuccess(value: ChatRoom?) { + CoroutineScope(Dispatchers.Main).launch { + try { + batchJoinRoom(joinRoomIds) EaseRepository.instance.reconnectionLoadMessages() EaseRepository.instance.fetchChatRoomMutedStatus() - } - - override fun onError(error: Int, errorMsg: String?) { - if (error == Error.CHATROOM_ALREADY_JOINED) { - EaseRepository.instance.reconnectionLoadMessages() - EaseRepository.instance.fetchChatRoomMutedStatus() + }catch (e:ChatError){ + if (e.code == Error.CHATROOM_ALREADY_JOINED) { + ThreadManager.instance.runOnMainThread { + EaseRepository.instance.reconnectionLoadMessages() + EaseRepository.instance.fetchChatRoomMutedStatus() + } } } - }) + } } } @@ -666,34 +711,51 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: } fun sendTextMessage(content: String) { - val message = ChatMessage.createTxtSendMessage(content, chatRoomId) - sendMessage(message) + CoroutineScope(Dispatchers.Main).launch { + try { + val msg = ChatMessage.createSendMessage(ChatMessage.Type.TXT) + val txtBody = TextMessageBody(content) + msg.addBody(txtBody) + batchSendMsg(sendRoomIds, msg) + } catch (e: ChatError) { + if (e.code == Error.MESSAGE_INCLUDE_ILLEGAL_CONTENT) { + ThreadManager.instance.runOnMainThread { + Toast.makeText(context, context.getString(R.string.fcr_hyphenate_im_message_incloud_illegal_content), Toast.LENGTH_SHORT).show() + } + } + } + } } fun sendImageMessage(uri: Uri) { - val message = ChatMessage.createImageSendMessage(uri, false, chatRoomId) - sendMessage(message) + CoroutineScope(Dispatchers.Main).launch { + try { + val img = FileHelper.getInstance().formatInUri(uri) + val message = ChatMessage.createSendMessage(ChatMessage.Type.IMAGE) + val body = ImageMessageBody(img) + body.isSendOriginalImage = false + message.addBody(body) + batchSendMsg(sendRoomIds, message) + } catch (e: ChatError) { + if (e.code == Error.MESSAGE_INCLUDE_ILLEGAL_CONTENT) { + ThreadManager.instance.runOnMainThread { + Toast.makeText(context, context.getString(R.string.fcr_hyphenate_im_message_incloud_illegal_content), Toast.LENGTH_SHORT).show() + } + } + } + } } - private fun sendMessage(message: ChatMessage) { - if (!(EaseRepository.instance.isInit && EaseRepository.instance.isLogin)) { - Toast.makeText(context, context.getString(R.string.fcr_hyphenate_im_send_message_failed) + ":" + context.getString(R.string.fcr_hyphenate_im_login_chat_failed), Toast.LENGTH_SHORT).show() - return - } + private suspend fun sendMsg(message: ChatMessage) = suspendCoroutine { cont -> setExtBeforeSend(message) message.chatType = ChatMessage.ChatType.ChatRoom message.setMessageStatusCallback(object : CallBack { override fun onSuccess() { - + cont.resume(message) } override fun onError(code: Int, error: String?) { - if (code == Error.MESSAGE_INCLUDE_ILLEGAL_CONTENT) { - ThreadManager.instance.runOnMainThread { - Toast.makeText(context, context.getString(R.string.fcr_hyphenate_im_message_incloud_illegal_content), Toast.LENGTH_SHORT).show() - } - } - EMLog.e(TAG, "onMessageError:$code = $error") + cont.resumeWithException(ChatError(code, error ?: "")) } override fun onProgress(progress: Int, status: String?) { @@ -701,7 +763,19 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: } }) ChatClient.getInstance().chatManager().sendMessage(message) + } + + private suspend fun batchSendMsg(roomIds: List, message:ChatMessage):List { + val list = mutableListOf() + roomIds.forEach { + val msg = ChatMessage.createSendMessage(message.type) + msg.to = it + msg.addBody(message.body) + val ret = sendMsg(msg) + list.add(ret) + } refreshUI() + return list } private fun setExtBeforeSend(message: ChatMessage) { @@ -760,4 +834,22 @@ class ChatViewPager(context: Context, attributeSet: AttributeSet?, defStyleAttr: } } + + fun setRecvRoomIds(recvRoomIds: List) { + this.recvRoomIds = recvRoomIds + EaseRepository.instance.recvRoomIds = recvRoomIds + + } + + fun setSendRoomIds(sendRoomIds: List) { + this.sendRoomIds = sendRoomIds + } + + fun setUserRoomIds(userRoomIds: List) { + this.userRoomIds = userRoomIds + } + + fun setChatGroupUuids(chatGroupUuids: List) { + this.chatGroupUuids = chatGroupUuids + } } \ No newline at end of file diff --git a/AgoraEduUIKit/src/main/java/io/agora/agoraeduuikit/impl/chat/ChatWidget.kt b/AgoraEduUIKit/src/main/java/io/agora/agoraeduuikit/impl/chat/ChatWidget.kt index 49ac62c76..2cc3fc000 100644 --- a/AgoraEduUIKit/src/main/java/io/agora/agoraeduuikit/impl/chat/ChatWidget.kt +++ b/AgoraEduUIKit/src/main/java/io/agora/agoraeduuikit/impl/chat/ChatWidget.kt @@ -8,6 +8,9 @@ abstract class ChatWidget : AgoraBaseWidget() { protected val userIdKey = "userId" protected val appNameKey = "appName" protected val chatRoomIdKey = "chatRoomId" + protected val sendRoomIdKey = "sendChatRoomIds" + protected val recvRoomIdKey = "receiveChatRoomIds" + protected val chatGroupUuidsKey = "chatGroupUuids" var hideIconSize = 0 diff --git a/app/build.gradle b/app/build.gradle index 827d577a0..d6e360774 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -41,6 +41,10 @@ android { targetCompatibility JavaVersion.VERSION_1_8 } + kotlinOptions { + jvmTarget = "1.8" + } + packagingOptions { exclude 'META-INF/edu_release.kotlin_module' } @@ -53,6 +57,7 @@ android { viewBinding { enabled = true } + namespace 'io.agora.education' } static def releaseTime() { @@ -73,7 +78,7 @@ dependencies { implementation project(path: ':AgoraClassSDK') implementation project(path: ':AgoraEduUIKit') if (readyPublishGithub.toBoolean()) { - implementation "io.github.agoraio-community:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" + implementation "io.github.agora-apaas:AgoraEduCore:${rootProject.ext.dependencies.EduCore}" } else { implementation project(path: ':AgoraEduCore') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index b57b85b0e..3a4d6c19b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,6 +1,5 @@ - + @@ -25,11 +24,11 @@ android:configChanges="keyboardHidden|screenSize|orientation|screenLayout" android:exported="true" android:windowSoftInputMode="adjustPan|stateAlwaysHidden"> - - + + - - + + + android:windowSoftInputMode="adjustPan|stateAlwaysHidden" > + + + + + + { override fun onSuccess(info: ConfigData?) { // Use authentication info from server instead @@ -640,9 +645,9 @@ class FcrMainActivity : BaseActivity(), View.OnClickListener { val config = AgoraEduLaunchConfig( userName, - userUuid, + userName, + roomName, roomName, - roomUuid, roleType, roomType, it.token, @@ -831,9 +836,9 @@ class FcrMainActivity : BaseActivity(), View.OnClickListener { private fun notifyBtnJoinEnable(enable: Boolean) { runOnUiThread { if (tvRoomType.text == getString(R.string.large_class_vocational)) { - btnJoin.isEnabled = enable && roomNameValid && userNameNameValid && roomTypeValid && roleTypeValid && serviceTypeValid + btnJoin.isEnabled = enable && roomNameValid && userNameNameValid && serviceTypeValid } else { - btnJoin.isEnabled = enable && roomTypeValid && roleTypeValid + btnJoin.isEnabled = enable } } } diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 0d8f93bdd..cc4e35ccc 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1,15 +1,15 @@ 声网灵动课堂 - 房间 + 房间号 房间Id - 昵称 + 用户名 用户Id 类型 角色 - 请输入房间名 + 请输入房间号 请输入房间Id - 请输入昵称 + 请输入用户名 请输入用户Id 请选择课堂类型 请选择角色类型 diff --git a/build.gradle b/build.gradle index 3559663bc..7611ec30c 100644 --- a/build.gradle +++ b/build.gradle @@ -16,7 +16,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:7.4.2' + classpath 'com.android.tools.build:gradle:8.6.0' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' } diff --git a/config.gradle b/config.gradle index 512d0192a..1c5602ace 100644 --- a/config.gradle +++ b/config.gradle @@ -2,7 +2,7 @@ ext { //def fcrApaasVersion = "2.8.0" sdkVersion = [// TODO versionName 如果以'-SNAPSHOT'结尾,aar将会被发送到SNAPSHOT仓库 - 'appVersion':"2.8.102" + 'appVersion':"2.8.103" ] maven = [ diff --git a/gradle.properties b/gradle.properties index a0727f5b8..7e05be4ec 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,3 +8,6 @@ android.injected.testOnly=false org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" android.enableJetifier=true android.useAndroidX=true +android.defaults.buildfeatures.buildconfig=true +android.nonTransitiveRClass=false +android.nonFinalResIds=false diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0bea740ab..8737e8b47 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Mon Aug 09 16:15:03 BST 2021 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME \ No newline at end of file