채팅 - 읽지 않은 메시지 알림

변현섭·2023년 9월 10일
0

저번 포스팅에서 실시간으로 메시지를 주고 받는 기능까지 추가해보았습니다. 그러나 현재 상태로는 새로운 메시지가 도착했는지 알 수 없습니다. 그래서 이번 포스팅에서는 새로운 메시지가 있음을 알려주는 기능과 메시지를 읽지 않은 유저의 수를 표시하는 기능을 추가해보겠습니다.

1. 새로운 메시지 알림

각 채팅방마다 본인이 읽지 않은 메시지의 개수를 확인할 수 있는 기능을 추가하자.

① MessageModel에 readerUid 필드를 추가한다.

  • 메시지를 읽은 유저의 uid를 저장하는 용도이므로 반드시 Mutable Map을 사용해야 한다.
  • 참고로, 파이어베이스 Realtime Database에 사용할 data model에는 List를 사용할 수 없다.
val readerUids: MutableMap<String, Boolean> = mutableMapOf(),

② ChatRoom에 unreadCount 필드를 추가한다.

val unreadCount : Int? = 0

③ chat_listview_item.xml 파일을 아래와 같이 수정한다.

  • 읽지 않은 메시지가 없을 때에는 아무 것도 표시하지 않기 위해 visibility="gone" 속성을 사용한다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/list_border"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp">

        <TextView
            android:id="@+id/lvChatRoomName"
            android:text="ChatRoom Name"
            android:textStyle="bold"
            android:textColor="#000000"
            android:textSize="20sp"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="10dp"
            android:layout_marginTop="10dp"
            android:layout_marginBottom="5dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

        <FrameLayout
            android:id="@+id/unreadMessageContainer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:visibility="gone"
            android:background="@drawable/message_box">

            <TextView
                android:id="@+id/unreadMessageCountTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="0"
                android:gravity="center"
                android:layout_marginHorizontal="8dp"
                android:layout_marginVertical="5dp"
                android:textSize="20sp"
                android:textColor="#000000" />

        </FrameLayout>

    </LinearLayout>

    <TextView
        android:id="@+id/lvUserListArea"
        android:text="User List"
        android:textSize="15sp"
        android:layout_marginHorizontal="40dp"
        android:layout_marginBottom="20dp"
        android:maxWidth="170dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</LinearLayout>

④ ChatListFragment의 getChatRoomList를 아래와 같이 수정하고, getUnreadMessageCount 메서드를 추가한다.

  • 내가 속한 채팅방 리스트를 받아온 뒤 각 채팅방의 메시지를 검사하여 읽지 않은 메시지의 개수를 count한다.
  • 여기서 읽지 않은 메시지란, MessageModel의 readerUid 맵에 나의 uid가 없는 경우를 의미한다.
private fun getChatRoomList() {
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                chatRoomList.clear()
                for (datamModel in dataSnapshot.children) {
                    val chatRoom = datamModel.getValue(ChatRoom::class.java)
                    chatRoomList.add(chatRoom!!)
                }
                for(chatRoom in chatRoomList) {
                    getUnreadMessageCount(chatRoom.chatRoomId!!)
                }

                listViewAdapter.notifyDataSetChanged()
            }

            override fun onCancelled(databseError: DatabaseError) {
                Log.w("MyMessage", "onCancelled", databseError.toException())
            }
        }
        FirebaseRef.chatRoom.child(FirebaseAuthUtils.getUid()).addValueEventListener(postListener)
    }

    private fun getUnreadMessageCount(chatRoomId : String) {
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                var count = 0
                for (datamModel in dataSnapshot.children) {
                    val uidList = datamModel.child("readerUid").getValue(object : GenericTypeIndicator<MutableMap<String, Boolean>>() {})

                    if (uidList != null) {
                        Log.d("readerUid", uidList.toString())
                        if (!uidList.containsKey(FirebaseAuthUtils.getUid())) {
                            // readerUid에 내 uid가 없으면
                            count++
                        }
                    }
                }
                FirebaseRef.chatRoom.child(FirebaseAuthUtils.getUid()).child(chatRoomId).child("unreadCount").setValue(count)
            }

            override fun onCancelled(databaseError: DatabaseError) {
                Log.w("MyMessage", "onCancelled", databaseError.toException())
            }
        }
        FirebaseRef.message.child(chatRoomId).addValueEventListener(postListener)
    }

⑤ ChatRoomAdapter를 아래와 같이 수정한다.

  • 읽지 않은 메시지가 있을 경우, 읽지 않은 메시지의 개수를 채팅방 제목 옆에 표시한다.
  • 단, 읽지 않은 메시지가 없을 경우, 아무 것도 표시되지 않도록 visibility를 조절한다.
class ChatRoomAdapter(private val context: Context, private val dataList : List<ChatRoom>) : BaseAdapter() {
    override fun getCount(): Int {
        return dataList.size
    }

    override fun getItem(position: Int): Any {
        return dataList[position]
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        var convertView = convertView

        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.chat_listview_item, parent, false)
        }
        val listViewChatRoomName = convertView?.findViewById<TextView>(R.id.lvChatRoomName)
        val listViewUserList = convertView?.findViewById<TextView>(R.id.lvUserListArea)
        val listViewUnreadCount = convertView?.findViewById<TextView>(R.id.unreadMessageCountTextView)
        val unreadMessageContainer = convertView?.findViewById<FrameLayout>(R.id.unreadMessageContainer)

        listViewUnreadCount!!.text = dataList[position].unreadCount.toString()
        val unreadCount = listViewUnreadCount!!.text.toString().toInt()

        if (unreadCount > 0) {
            unreadMessageContainer!!.visibility = View.VISIBLE
            listViewUnreadCount.visibility = View.VISIBLE
        } else {
            unreadMessageContainer!!.visibility = View.GONE
            listViewUnreadCount.visibility = View.GONE
        }

        listViewChatRoomName!!.text = dataList[position].roomName
        listViewUserList!!.text = dataList[position].userList

        return convertView!!
    }
}

⑥ 채팅방에 들어가면 모든 메시지의 readerUid Mutable Map에 본인의 UID를 추가해야 한다. ChatRoomActivity를 아래와 같이 수정한다.

  • readerUidMap에 채팅방에 들어온 유저의 uid(key)에 true(value)를 저장한다.
  • 메시지 전송 버튼을 클릭할 때 MessageModel의 readerUids 값으로, empty 맵을 넣는다.
  • getMessageList에서 MessageModel의 readerUids 값으로 readerUidMap을 넣는다.
  • 뒤로 가기 버튼을 누르면 MainActivity로 바로 전환되게 하고(초대 등을 누른 경우 뒤로 가기를 눌러도 채팅방이 안 나가지기 때문), 해당 유저의 UID를 readerUidMap에서 제거한다.
  • 뒤로 가기 버튼을 눌렀을 때 UID를 readerUidMap에서 제거하지 않으면, 채팅방에 입장한 상태가 아니더라도 앱이 동작 중이면, readerUidMap에 uid가 그대로 남아 메시지를 읽은 것으로 간주한다.
class ChatRoomActivity : AppCompatActivity() {

    lateinit var count : String
    lateinit var messageAdapter : MessageAdapter
    lateinit var recyclerView : RecyclerView
    val messageList = mutableListOf<MessageModel>()
    val readerUidMap = mutableMapOf<String, Boolean>()

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat_room)

        readerUidMap[FirebaseAuthUtils.getUid()] = true

        val roomName = findViewById<TextView>(R.id.chatRoomName)
        val nickNameList = findViewById<TextView>(R.id.nickNameList)
        val inviteBtn = findViewById<Button>(R.id.invite)
        val userCount = findViewById<TextView>(R.id.userCount)

        // Intent로부터 데이터를 가져옴
        val chatRoomName = intent.getStringExtra("chatRoomName")
        roomName.text = chatRoomName

        val roomId = intent.getStringExtra("chatRoomId")
        val chatRoomId = roomId

        val userList = intent.getStringExtra("userList")
        nickNameList.text = userList

        CoroutineScope(Dispatchers.IO).launch {
            val response = getUserCount(chatRoomId!!)
            Log.d("userCount", response.toString())
            if (response.isSuccess) {
                count = response.result.toString()
                userCount.text = count
            } else {
                Log.d("UserListFragment", "유저의 정보를 불러오지 못함")
            }
        }

        recyclerView = findViewById<RecyclerView>(R.id.messageRV)
        messageAdapter = MessageAdapter(this, messageList)
        recyclerView.adapter = messageAdapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        getMessageList(chatRoomId!!)
        Log.d("MessageList", messageList.toString())

        inviteBtn.setOnClickListener {
            val intent = Intent(this, InviteActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            intent.putExtra("chatRoomName", chatRoomName)
            intent.putExtra("nickNameList", userList)
            startActivity(intent)
        }

        val participantBtn = findViewById<ImageView>(R.id.participants)
        participantBtn.setOnClickListener {
            val intent = Intent(this, ParticipantsActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            startActivity(intent)
        }

        val message = findViewById<TextInputEditText>(R.id.message)
        val sendBtn = findViewById<ImageView>(R.id.send)

        lateinit var myNickName : String
        lateinit var myProfileUrl : String
        lateinit var messageModel: MessageModel
        val myUid = FirebaseAuthUtils.getUid()

        sendBtn.setOnClickListener {
            val contents = message.text.toString()
            val sendTime = getSendTime()
            CoroutineScope(Dispatchers.IO).launch {
                val response = getUserInfo(myUid)
                if (response.isSuccess) {
                    myNickName = response.result?.nickName.toString()
                    myProfileUrl = response.result?.imgUrl.toString()
                    val readerUids = mutableMapOf<String, Boolean>()
                    messageModel = MessageModel(myUid, myNickName, myProfileUrl, contents, sendTime, readerUids)
                    FirebaseRef.message.child(chatRoomId!!).push().setValue(messageModel)
                } else {
                    Log.d("ChatRoomActivity", "유저의 정보를 불러오지 못함")
                }
            }
            message.text?.clear()
        }
    }

    override fun onBackPressed() {
        readerUidMap.remove(FirebaseAuthUtils.getUid())
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }

    private suspend fun getUserCount(roomId: String) : BaseResponse<String> {
        return RetrofitInstance.chatApi.getUserCount(roomId)
    }

    private suspend fun getUserInfo(uid: String): BaseResponse<GetUserRes> {
        return RetrofitInstance.myPageApi.getUserInfo(uid)
    }

    //메시지 보낸 시각 정보 반환
    @RequiresApi(Build.VERSION_CODES.O)
    private fun getSendTime(): String {
        try {
            val localDateTime = LocalDateTime.now()
            val dateTimeFormatter = DateTimeFormatter.ofPattern("M/d  h:mm  a")
            return localDateTime.format(dateTimeFormatter)
        } catch (e: Exception) {
            e.printStackTrace()
            throw Exception("시간 정보를 불러오지 못함")
        }
    }

    private fun getMessageList(chatRoomId: String) {
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                messageList.clear()
                val newMessages = mutableListOf<MessageModel>() // 새로운 메시지를 저장할 리스트 생성

                for (dataModel in dataSnapshot.children) {
                    val messageModel = dataModel.getValue(MessageModel::class.java)
                    if (messageModel != null) {
                        if (messageModel.senderUid != FirebaseAuthUtils.getUid()) {
                            messageModel.viewType = MessageModel.VIEW_TYPE_YOU
                        }

                        // 새로운 메시지 ID 생성
                        val messageId = dataModel.key
                        if (messageId != null) {
                            FirebaseRef.message.child(chatRoomId!!).child(messageId).child("readerUid")
                                .updateChildren(readerUidMap as Map<String, Boolean>)
                        }

                        newMessages.add(messageModel)
                    }
                }

                messageList.addAll(newMessages)
                messageAdapter.notifyDataSetChanged()
                Log.d("MessageList", messageList.toString())

                recyclerView.post {
                    recyclerView.scrollToPosition(recyclerView.adapter?.itemCount?.minus(1) ?: 0)
                }
            }

            override fun onCancelled(databaseError: DatabaseError) {
                Log.w("MyMessage", "onCancelled", databaseError.toException())
            }
        }
        FirebaseRef.message.child(chatRoomId).addValueEventListener(postListener)
    }
}

코드를 실행시켜보면, 읽지 않은 메시지의 개수가 실시간으로 추가되는 것을 확인해 볼 수 있을 것이다. 또한 채팅방에 들어갔다가 나오면, 채팅방의 모든 메시지를 읽은 것으로 처리해야 한다.

2. 읽지 않은 유저 수 알려주기

① message_recycler_view_item.xml 파일을 아래와 같이 수정한다.

  • 이번에도 모두가 읽은 경우 아무 것도 표시하지 않기 위해 Visiblity 속성을 이용하겠다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/messageContentsArea"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="35dp"
            android:layout_marginEnd="8dp"
            android:background="@drawable/message_box"
            android:ellipsize="end"
            android:maxWidth="230dp"
            android:padding="10dp"
            android:text="my text message"
            android:textSize="15sp"
            app:layout_constraintEnd_toStartOf="@+id/messageProfileArea"
            app:layout_constraintTop_toTopOf="parent" />

        <FrameLayout
            android:id="@+id/unreadUserCountContainer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/messageContentsArea">

            <TextView
                android:id="@+id/unreadUserCountTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginHorizontal="5dp"
                android:layout_marginBottom="20dp"
                android:gravity="center"
                android:text="10"
                android:textColor="#ffd400"
                android:textSize="10sp" />
        </FrameLayout>

        <TextView
            android:id="@+id/dateTime"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="5dp"
            android:layout_marginEnd="15dp"
            android:text="9/7 7:01 AM"
            android:textSize="10sp"
            app:layout_constraintEnd_toStartOf="@+id/messageProfileArea"
            app:layout_constraintTop_toBottomOf="@+id/messageContentsArea" />

        <ImageView
            android:id="@+id/messageProfileArea"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginTop="15dp"
            android:layout_marginRight="15dp"
            android:src="@drawable/profile"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/messageNickName"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginEnd="17dp"
            android:layout_marginBottom="2dp"
            android:gravity="center"
            android:text="nickname"
            android:textColor="@color/black"
            android:textSize="15sp"
            app:layout_constraintBottom_toTopOf="@+id/messageContentsArea"
            app:layout_constraintEnd_toStartOf="@+id/messageProfileArea" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>

② message_recycler_view_item2.xml도 마찬가지로 수정해준다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/messageContentsArea2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="8dp"
            android:layout_marginTop="35dp"
            android:background="@drawable/your_message_box"
            android:ellipsize="end"
            android:maxWidth="230dp"
            android:padding="10dp"
            android:text="my text message"
            android:textSize="12sp"
            app:layout_constraintStart_toEndOf="@+id/messageProfileArea2"
            app:layout_constraintTop_toTopOf="parent" />

        <FrameLayout
            android:id="@+id/unreadUserCountContainer2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:visibility="gone"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toEndOf="@+id/messageContentsArea2">

            <TextView
                android:id="@+id/unreadUserCountTextView2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginHorizontal="5dp"
                android:layout_marginBottom="20dp"
                android:gravity="center"
                android:text="10"
                android:textColor="#ffd400"
                android:textSize="10sp" />
        </FrameLayout>

        <TextView
            android:id="@+id/dateTime2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="9/7 7:01 AM"
            android:textSize="10sp"
            android:layout_marginTop="5dp"
            android:layout_marginStart="15dp"
            app:layout_constraintStart_toEndOf="@+id/messageProfileArea2"
            app:layout_constraintTop_toBottomOf="@+id/messageContentsArea2" />

        <ImageView
            android:id="@+id/messageProfileArea2"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_marginTop="15dp"
            android:layout_marginLeft="15dp"
            android:src="@drawable/profile"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/messageNickName2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="17dp"
            android:gravity="center"
            android:text="nickname"
            android:textColor="@color/black"
            android:textSize="15sp"
            app:layout_constraintBottom_toTopOf="@+id/messageContentsArea2"
            app:layout_constraintStart_toEndOf="@+id/messageProfileArea2" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>

③ MessageModel에 unreadUserCount 필드를 추가한다.

var unreadUserCount : Int = 0, // 메시지를 읽지 않은 사람의 수

④ MessageAdapter를 아래와 같이 수정한다.

class MessageAdapter(private val context: Context, val items: MutableList<MessageModel>)
    : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    class ItemViewHolder1(private val view: View) : RecyclerView.ViewHolder(view) {
        val contents : TextView = view.findViewById(R.id.messageContentsArea)
        val dateTime : TextView = view.findViewById(R.id.dateTime)
        val nickName : TextView = view.findViewById(R.id.messageNickName)
        val profile : ImageView = view.findViewById(R.id.messageProfileArea)
        val unreadUserContainer : FrameLayout = view.findViewById(R.id.unreadUserCountContainer)
        val unreadUserCount : TextView = view.findViewById(R.id.unreadUserCountTextView)
    }

    class ItemViewHolder2(private val view: View) : RecyclerView.ViewHolder(view) {
        val contents : TextView = view.findViewById(R.id.messageContentsArea2)
        val dateTime : TextView = view.findViewById(R.id.dateTime2)
        val nickName : TextView = view.findViewById(R.id.messageNickName2)
        val profile : ImageView = view.findViewById(R.id.messageProfileArea2)
        val unreadUserContainer : FrameLayout = view.findViewById(R.id.unreadUserCountContainer2)
        val unreadUserCount : TextView = view.findViewById(R.id.unreadUserCountTextView2)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val adapterLayout : View?
        return when(viewType) {
            MessageModel.VIEW_TYPE_ME -> {
                adapterLayout = LayoutInflater.from(parent.context).inflate(R.layout.message_recycler_view_item, parent, false)
                ItemViewHolder1(adapterLayout)
            }
            MessageModel.VIEW_TYPE_YOU -> {
                adapterLayout = LayoutInflater.from(parent.context).inflate(R.layout.message_recycler_view_item2, parent, false)
                ItemViewHolder2(adapterLayout)
            }
            else -> throw RuntimeException("Invalid View Type Error")
        }

    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val item = items[position]
        when (item.viewType) {
            MessageModel.VIEW_TYPE_ME -> {
                (holder as ItemViewHolder1).contents.text = item.contents
                holder.dateTime.text = item.sendTime
                holder.nickName.text = item.senderNickName
                holder.unreadUserCount.text = item.unreadUserCount.toString()


                if (item.unreadUserCount.toString().toInt() > 0) {
                    holder.unreadUserContainer!!.visibility = View.VISIBLE
                    holder.unreadUserCount.visibility = View.VISIBLE
                } else {
                    holder.unreadUserContainer!!.visibility = View.GONE
                    holder.unreadUserCount.visibility = View.GONE
                }

                if (item.senderProfileUrl != "null") {
                    Glide.with(context)
                        .load(item.senderProfileUrl)
                        .into(holder.profile)
                } else {
                    holder.profile.setImageResource(R.drawable.profile)
                }
            }

            MessageModel.VIEW_TYPE_YOU -> {
                (holder as ItemViewHolder2).contents.text = item.contents
                holder.dateTime.text = item.sendTime
                holder.nickName.text = item.senderNickName
                holder.unreadUserCount.text = item.unreadUserCount.toString()

                if (item.unreadUserCount.toString().toInt() > 0) {
                    holder.unreadUserContainer!!.visibility = View.VISIBLE
                    holder.unreadUserCount.visibility = View.VISIBLE
                } else {
                    holder.unreadUserContainer!!.visibility = View.GONE
                    holder.unreadUserCount.visibility = View.GONE
                }

                if (item.senderProfileUrl != "null") {
                    Glide.with(context)
                        .load(item.senderProfileUrl)
                        .into(holder.profile)
                } else {
                    holder.profile.setImageResource(R.drawable.profile)
                }
            }
        }
    }

    override fun getItemCount(): Int {
        return items.size
    }

    override fun getItemViewType(position: Int): Int {
        return items[position].viewType
    }
}

⑤ ChatRoomActivity를 아래와 같이 수정한다.

  • 메시지를 읽은 사람의 수를 채팅방 전체 유저 수에서 빼면 된다.
class ChatRoomActivity : AppCompatActivity() {

    lateinit var count : String // 채팅방 참여 인원수
    lateinit var messageAdapter : MessageAdapter
    lateinit var recyclerView : RecyclerView
    val messageList = mutableListOf<MessageModel>()
    val readerUidMap = mutableMapOf<String, Boolean>()

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat_room)

        readerUidMap[FirebaseAuthUtils.getUid()] = true

        val roomName = findViewById<TextView>(R.id.chatRoomName)
        val nickNameList = findViewById<TextView>(R.id.nickNameList)
        val inviteBtn = findViewById<Button>(R.id.invite)
        val userCount = findViewById<TextView>(R.id.userCount)

        // Intent로부터 데이터를 가져옴
        val chatRoomName = intent.getStringExtra("chatRoomName")
        roomName.text = chatRoomName

        val roomId = intent.getStringExtra("chatRoomId")
        val chatRoomId = roomId

        val userList = intent.getStringExtra("userList")
        nickNameList.text = userList

        CoroutineScope(Dispatchers.IO).launch {
            val response = getUserCount(chatRoomId!!)
            Log.d("userCount", response.toString())
            if (response.isSuccess) {
                count = response.result.toString()
                userCount.text = count
            } else {
                Log.d("UserListFragment", "유저의 정보를 불러오지 못함")
            }
        }

        recyclerView = findViewById<RecyclerView>(R.id.messageRV)
        messageAdapter = MessageAdapter(this, messageList)
        recyclerView.adapter = messageAdapter
        recyclerView.layoutManager = LinearLayoutManager(this)

        getMessageList(chatRoomId!!)
        Log.d("MessageList", messageList.toString())

        inviteBtn.setOnClickListener {
            val intent = Intent(this, InviteActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            intent.putExtra("chatRoomName", chatRoomName)
            intent.putExtra("nickNameList", userList)
            startActivity(intent)
        }

        val participantBtn = findViewById<ImageView>(R.id.participants)
        participantBtn.setOnClickListener {
            val intent = Intent(this, ParticipantsActivity::class.java)
            intent.putExtra("chatRoomId", chatRoomId)
            startActivity(intent)
        }

        val message = findViewById<TextInputEditText>(R.id.message)
        val sendBtn = findViewById<ImageView>(R.id.send)

        lateinit var myNickName : String
        lateinit var myProfileUrl : String
        lateinit var messageModel: MessageModel
        val myUid = FirebaseAuthUtils.getUid()

        sendBtn.setOnClickListener {
            val contents = message.text.toString()
            if(contents.isEmpty()) {
                Toast.makeText(this, "메시지를 입력해주세요", Toast.LENGTH_SHORT).show()
            }
            else {
                val sendTime = getSendTime()
                CoroutineScope(Dispatchers.IO).launch {
                    val response = getUserInfo(myUid)
                    if (response.isSuccess) {
                        myNickName = response.result?.nickName.toString()
                        myProfileUrl = response.result?.imgUrl.toString()
                        val readerUids = mutableMapOf<String, Boolean>()
                        messageModel = MessageModel(myUid, myNickName, myProfileUrl, contents,
                            sendTime, readerUids, 0)
                        Log.d("readerUid", readerUids.size.toString())
                        FirebaseRef.message.child(chatRoomId!!).push().setValue(messageModel)
                    } else {
                        Log.d("ChatRoomActivity", "유저의 정보를 불러오지 못함")
                    }
                }
                message.text?.clear()
            }
        }
    }

    override fun onBackPressed() {
        readerUidMap.remove(FirebaseAuthUtils.getUid())
        val intent = Intent(this, MainActivity::class.java)
        startActivity(intent)
    }

    private suspend fun getUserCount(roomId: String) : BaseResponse<String> {
        return RetrofitInstance.chatApi.getUserCount(roomId)
    }

    private suspend fun getUserInfo(uid: String): BaseResponse<GetUserRes> {
        return RetrofitInstance.myPageApi.getUserInfo(uid)
    }

    //메시지 보낸 시각 정보 반환
    @RequiresApi(Build.VERSION_CODES.O)
    private fun getSendTime(): String {
        try {
            val localDateTime = LocalDateTime.now()
            val dateTimeFormatter = DateTimeFormatter.ofPattern("M/d  h:mm  a")
            return localDateTime.format(dateTimeFormatter)
        } catch (e: Exception) {
            e.printStackTrace()
            throw Exception("시간 정보를 불러오지 못함")
        }
    }

    private fun getMessageList(chatRoomId: String) {
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                messageList.clear()
                val newMessages = mutableListOf<MessageModel>() // 새로운 메시지를 저장할 리스트 생성

                for (dataModel in dataSnapshot.children) {
                    val messageModel = dataModel.getValue(MessageModel::class.java)
                    val messageId = dataModel.key
                    if (messageModel != null) {
                        if (messageId != null) {
                            // readerUidMap을 업데이트
                            FirebaseRef.message.child(chatRoomId!!).child(messageId).child("readerUids")
                                .updateChildren(readerUidMap as Map<String, Boolean>)
                                .addOnCompleteListener { readerUidTask ->
                                    if (readerUidTask.isSuccessful) {
                                        // readerUid 업데이트가 성공한 경우 unreadUserCount를 계산하여 업데이트합니다.
                                        val unreadUserCount = count.toInt() - messageModel.readerUids.size
                                        FirebaseRef.message.child(chatRoomId!!).child(messageId).child("unreadUserCount")
                                            .setValue(unreadUserCount)
                                    } else {
                                        Log.d("ChatRoomActivity", "reader UID를 업데이트하지 못함")
                                    }
                                }
                        }

                        if (messageModel.senderUid != FirebaseAuthUtils.getUid()) {
                            messageModel.viewType = MessageModel.VIEW_TYPE_YOU
                        }
                        newMessages.add(messageModel)
                    }
                }

                messageList.addAll(newMessages)
                messageAdapter.notifyDataSetChanged()
                Log.d("MessageList", messageList.toString())

                recyclerView.post {
                    recyclerView.scrollToPosition(recyclerView.adapter?.itemCount?.minus(1) ?: 0)
                }
            }

            override fun onCancelled(databaseError: DatabaseError) {
                Log.w("MyMessage", "onCancelled", databaseError.toException())
            }
        }
        FirebaseRef.message.child(chatRoomId).addValueEventListener(postListener)
    }
}

이제 코드를 실행해보면, 아래와 같이 읽지 않은 유저의 수가 메시지 옆에 표시될 것이다.

profile
Java Spring, Android Kotlin, Node.js, ML/DL 개발을 공부하는 인하대학교 정보통신공학과 학생입니다.

0개의 댓글