채팅 - 유저 초대하기

변현섭·2023년 9월 7일
0

이번 포스팅에서는 채팅방의 초대 버튼을 클릭해 유저를 초대할 수 있는 기능을 추가해보도록 하겠습니다. 또한 채팅방에 참여한 유저의 수와 목록을 확인할 수 있는 기능도 추가해보겠습니다.

3. 채팅방에 초대하기

1) 초대 목록 구성하기

① chat 패키지 하위로, InviteActivity를 추가한다.

② activity_invite.xml 파일에 아래의 내용을 입력한다.

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/list_border"
    android:orientation="vertical"
    tools:context=".chat.InviteActivity">
    
    <TextView
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:text="초대할 유저를 선택하세요."
        android:gravity="center"
        android:layout_marginHorizontal="10dp"
        android:padding="5dp"
        android:background="@drawable/list_border"
        android:textColor="#000000"
        android:textStyle="bold"
        android:textSize="25sp"
        android:layout_marginBottom="10dp"/>

    <ListView
        android:id="@+id/inviteListView"
        android:layout_width="match_parent"
        android:layout_marginHorizontal="10dp"
        android:layout_height="0dp"
        android:layout_weight="1"/>
</LinearLayout>

③ adapter와 list의 generics, xml, api는 친구 목록 표시에 사용했던 것을 그대로 사용하도록 하겠다. InviteActivity에 아래의 내용을 입력한다.

class InviteActivity : AppCompatActivity() {

    // userProfileList를 nullable로 선언
    private var userProfileList: List<UserProfile>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_invite)

        // userProfileList 초기화는 API 응답 이후에 수행
        getAccessToken { accessToken ->
            if (accessToken.isNotEmpty()) {
                CoroutineScope(Dispatchers.IO).launch {
                    val response = getUsers(accessToken)
                    if (response.isSuccess) {
                        userProfileList = response.result
                        Log.d("UserProfileList", userProfileList.toString())
                        withContext(Dispatchers.Main) {
                            // UI 업데이트는 Main 스레드에서 수행
                            val adapter = ListViewAdapter(this@InviteActivity, userProfileList ?: emptyList())
                            val listview = findViewById<ListView>(R.id.inviteListView)
                            listview.adapter = adapter
                            adapter.notifyDataSetChanged()
                            Log.d("UserProfileList", userProfileList.toString())
                            listview.setOnItemClickListener { parent, view, position, id ->
                                
                            }
                        }
                    } else {
                        userProfileList = emptyList() // 초기화 실패 시 빈 리스트로 설정
                        Log.d("UserListFragment", "데이터 불러오기 실패")
                        val message = response.message
                        Log.d("UserListFragment", message)
                    }
                }
            } else {
                Log.e("UserListFragment", "Invalid Token")
            }
        }
    }

    private suspend fun getUsers(accessToken: String): BaseResponse<List<UserProfile>> {
        return RetrofitInstance.userApi.getUsers(accessToken)
    }

    private fun getAccessToken(callback: (String) -> Unit) {
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                val data = dataSnapshot.getValue(com.chrome.chattingapp.authentication.UserInfo::class.java)
                val accessToken = data?.accessToken ?: ""
                callback(accessToken)
            }

            override fun onCancelled(databaseError: DatabaseError) {
                Log.w("UserListFragment", "onCancelled", databaseError.toException())
            }
        }

        FirebaseRef.userInfo.child(FirebaseAuthUtils.getUid()).addListenerForSingleValueEvent(postListener)
    }
}

코드를 실행한 후, 채팅방의 초대 버튼을 클릭하면 아래와 같이 친구 목록이 나타날 것이다.

2) 친구 초대하기

① chat 패키지 하위로 dto 패키지를 생성하고 그 안에 AddUserReq data class를 생성한다.

data class AddUserReq(
    @SerializedName("uid")
    val uid : String,

    @SerializedName("roomId")
    val roomId : String,
)

② ChatApi에 아래의 API를 추가한다.

@POST("/chat/room/add")
suspend fun addUser(@Body addUserReq : AddUserReq) : BaseResponse<String>

@GET("/chat/userCount/{roomId}")
suspend fun getUserCount(@Path("roomId") roomId : String) : BaseResponse<String>

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

  • 초대 버튼을 클릭했을 때 초대화면으로 전환되고, 초대할 유저를 선택했을 때 다시 채팅방 화면으로 전환되려면, intent를 이용해 chatRoomId와 chatRoomName을 전달해야 한다.
  • 또한 채팅에 참여한 인원 수를 api를 이용해 설정해야 한다.
class ChatRoomActivity : AppCompatActivity() {

    lateinit var count : String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chat_room)

        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)
        val sendBtn = findViewById<ImageView>(R.id.send)

        // 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", "유저의 정보를 불러오지 못함")
            }
        }

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

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

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

  • 초대된 사람도 실시간으로 ChatListFragment에서 새롭게 생성된 채팅방을 확인할 수 있어야 한다.
class InviteActivity : AppCompatActivity() {

    // userProfileList를 nullable로 선언
    private var userProfileList: List<UserProfile>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_invite)

        val chatRoomId = intent.getStringExtra("chatRoomId")
        val chatRoomName = intent.getStringExtra("chatRoomName")
        val nickNameList = intent.getStringExtra("nickNameList")

        // userProfileList 초기화는 API 응답 이후에 수행
        getAccessToken { accessToken ->
            if (accessToken.isNotEmpty()) {
                CoroutineScope(Dispatchers.IO).launch {
                    val response = getUsers(accessToken)
                    if (response.isSuccess) {
                        userProfileList = response.result
                        Log.d("UserProfileList", userProfileList.toString())
                        withContext(Dispatchers.Main) {
                            // UI 업데이트는 Main 스레드에서 수행
                            val adapter = ListViewAdapter(this@InviteActivity, userProfileList ?: emptyList())
                            val listview = findViewById<ListView>(R.id.inviteListView)
                            listview.adapter = adapter
                            adapter.notifyDataSetChanged()
                            Log.d("UserProfileList", userProfileList.toString())
                            listview.setOnItemClickListener { parent, view, position, id ->
                                val newNickNameList = nickNameList + ", " + userProfileList!![position].nickName
                                val chatRoom = ChatRoom(chatRoomId, chatRoomName, newNickNameList)
                                val invitedUid = userProfileList!![position].uid
                                Log.d("ChatRoom", chatRoom.toString())
                                FirebaseRef.chatRoom.child(FirebaseAuthUtils.getUid()).child(chatRoomId!!).setValue(chatRoom)

                                val intent = Intent(this@InviteActivity, ChatRoomActivity::class.java)
                                intent.putExtra("chatRoomId", chatRoomId)
                                intent.putExtra("chatRoomName", chatRoomName)
                                intent.putExtra("userList", newNickNameList)
                                intent.putExtra("invitedUid", invitedUid)
                                
                                CoroutineScope(Dispatchers.IO).launch {
                                    val addUserReq = AddUserReq(userProfileList!![position].uid, chatRoomId)
                                    val response = addUser(addUserReq)
                                    if (response.isSuccess) {
                                        val nickName = response.result
                                        withContext(Dispatchers.Main) {
                                            FirebaseRef.chatRoom.child(invitedUid).child(chatRoomId!!).setValue(chatRoom)
                                            Toast.makeText(this@InviteActivity, nickName + " 님을 초대하였습니다.", Toast.LENGTH_SHORT).show()
                                        }
                                        startActivity(intent)
                                    } else {
                                        val message = response.message
                                        Log.d("InviteActivity", message)
                                        withContext(Dispatchers.Main) {
                                            Toast.makeText(this@InviteActivity, message, Toast.LENGTH_SHORT).show()
                                        }
                                    }
                                }
                            }
                        }
                    } else {
                        userProfileList = emptyList() // 초기화 실패 시 빈 리스트로 설정
                        Log.d("UserListFragment", "데이터 불러오기 실패")
                        val message = response.message
                        Log.d("UserListFragment", message)
                    }
                }
            } else {
                Log.e("UserListFragment", "Invalid Token")
            }
        }
    }

    private suspend fun getUsers(accessToken: String): BaseResponse<List<UserProfile>> {
        return RetrofitInstance.userApi.getUsers(accessToken)
    }

    private suspend fun addUser(addUserReq: AddUserReq) : BaseResponse<String> {
        return RetrofitInstance.chatApi.addUser(addUserReq)
    }

    private fun getAccessToken(callback: (String) -> Unit) {
        val postListener = object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                val data = dataSnapshot.getValue(com.chrome.chattingapp.authentication.UserInfo::class.java)
                val accessToken = data?.accessToken ?: ""
                callback(accessToken)
            }

            override fun onCancelled(databaseError: DatabaseError) {
                Log.w("UserListFragment", "onCancelled", databaseError.toException())
            }
        }

        FirebaseRef.userInfo.child(FirebaseAuthUtils.getUid()).addListenerForSingleValueEvent(postListener)
    }
}

이제 코드를 실행해보자. 채팅방에 입장하여 초대 버튼을 클릭하면 친구 목록이 나오고, 목록의 아이템을 클릭하여 해당 유저를 채팅방에 초대할 수 있다. 또한 채팅방의 Nickname List와 인원 수가 알맞게 변경되어야 한다.

4. 채팅방 참여자 목록 조회하기

① chat 디렉토리 하위로, ParticipantsActivity를 추가한다.

② activity_participants.xml 파일에 아래의 내용을 입력한다.

  • activity_invite.xml에서 사용한 레이아웃을 그대로 이용하겠다.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/list_border"
    android:orientation="vertical"
    tools:context=".chat.ParticipantsActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:text="참여자 목록"
        android:gravity="center"
        android:layout_marginHorizontal="10dp"
        android:padding="5dp"
        android:background="@drawable/list_border"
        android:textColor="#000000"
        android:textStyle="bold"
        android:textSize="25sp"
        android:layout_marginBottom="10dp"/>

    <ListView
        android:id="@+id/participantListView"
        android:layout_width="match_parent"
        android:layout_marginHorizontal="10dp"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    
</LinearLayout>

③ ChatRoomActivity에 Particiapnts 버튼에 대한 클릭 이벤트 리스너를 등록하자.

val participantBtn = findViewById<ImageView>(R.id.participants)

participantBtn.setOnClickListener {
    val intent = Intent(this, ParticipantsActivity::class.java)
    intent.putExtra("chatRoomId", chatRoomId)
    startActivity(intent)
}

④ ChatApi에 아래의 API를 추가한다.

@GET("/chat/room/{roomId}")
suspend fun getUserList(@Path("roomId") roomId : String) : BaseResponse<List<UserProfile>>

⑤ ParticipantActivity에 아래의 내용을 입력한다.

class ParticipantsActivity : AppCompatActivity() {

    private var participantsProfileList : List<UserProfile>? = null
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_participants)

        val chatRoomId = intent.getStringExtra("chatRoomId")

        CoroutineScope(Dispatchers.IO).launch {
            val response = getUserList(chatRoomId!!)
            if (response.isSuccess) {
                participantsProfileList = response.result
                Log.d("participantsProfileList", participantsProfileList.toString())
                withContext(Dispatchers.Main) {
                    val adapter = ListViewAdapter(this@ParticipantsActivity, participantsProfileList ?: emptyList())
                    val listview = findViewById<ListView>(R.id.participantListView)
                    listview.adapter = adapter
                    adapter.notifyDataSetChanged()

                    listview.setOnItemClickListener { parent, view, position, id ->
                        val imgUrl = participantsProfileList!![position].imgUrl
                        val nickName = participantsProfileList!![position].nickName
                        val intent = Intent(this@ParticipantsActivity, UserDetailActivity::class.java)
                        intent.putExtra("imgUrl", imgUrl)
                        intent.putExtra("nickName", nickName)
                        startActivity(intent)
                    }
                }
            } else {
                participantsProfileList = emptyList() // 초기화 실패 시 빈 리스트로 설정
                Log.d("ParticipantActivity", "데이터 불러오기 실패")
                val message = response.message
                Log.d("ParticipantActivity", message)
            }
        }
    }

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

코드를 실행시켜보면, 채팅방에서 Participant 버튼을 클릭했을 때 아래와 같이 채팅방에 참여한 유저의 목록이 나올 것이다. 또한 목록에서 유저를 클릭하면 해당 유저의 정보 화면으로 이동되어야 한다.

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

0개의 댓글