소개팅 앱 8. 푸시 메시지 전송하기

변현섭·2023년 8월 29일
0

드디어 소개팅 앱 만들기의 마지막 포스팅을 작성할 때가 되었습니다. 이번 포스팅에서 추가할 기능은 Long Click 이벤트 리스너와 실시간 쪽지 보내기 기능입니다.

1. Long Click 이벤트 리스너 사용하기

1) Long Click 이벤트 리스너 등록하기

① 기존에는 MatchingListActivity 파일에 리스트 뷰 아이템을 클릭하면, 매칭 여부를 알려주고 push 알림을 보냈다.

② 이제부터는 일반 클릭에 대해서는 push 알림만 보내고(일명 "찔러보기" 기능), Long Click을 했을 때에 Toast 메시지를 띄우는 것으로 변경해보자.

listview.setOnItemClickListener { parent, view, position, id ->
    val noticeModel = NoticeModel("제가 당신을 좋아하나봐요!", "저.. 어떠세요?")
    val pushNotice = PushNotice(noticeModel, myLikeUserInfo[position].token.toString())
    pushNotification(pushNotice)
}

listview.setOnItemLongClickListener { parent, view, position, id ->
    matchingChk(myLikeUserInfo[position].uid.toString())
    return@setOnItemLongClickListener(true)
}
  • return@setOnItemLongClickListener(true): 실행 중인 내부 루프를 빠져나오기 위해 사용하는 Label이다. 즉, 리스너가 true 값을 반환하면 해당 아이템에 대한 Long Click 이벤트가 소비되어 다른 처리를 방지할 수 있다.

③ 코드를 실행해보면, 매칭 리스트의 유저를 일반 클릭 시에는 push 알림이 보내질 것이고, 길게 누를 시에는 Toast 메시지가 나올 것이다.

2) 매칭된 유저 Long Click 시 AlertDialog 띄우기

① 매칭 리스트에서 유저를 Long Click했을 때, 만약 매칭된 유저라면, Android Dialog를 띄워주기로 하자. layout 디렉토리 하위로 dialog라는 이름의 xml파일을 추가한다.

② dialog.xml 파일의 레이아웃을 Linear Layout으로 변경하고 orientation을 vertical로 설정한다. 이후 아래의 내용을 추가한다.

<EditText
    android:id="@+id/message"
    android:layout_width="match_parent"
    android:layout_height="100dp"
    android:layout_margin="20dp"
    android:padding="7dp"
    android:hint="텍스트를 입력하세요" />
    
<Button
	android:id="@+id/send"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_margin="20dp"
    android:background="@color/pink"
    android:text="보내기"/>

③ EditText의 테두리를 만들어주기 위해 drawable 디렉토리 하위로 message_border이라는 이름의 리소스 파일을 생성하자.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">

    <item>
        <shape android:shape="rectangle">
            <solid android:color="@color/pink" />
        </shape>

    </item>

    <item android:top = "3dp"
        android:right= "3dp"
        android:left="3dp"
        android:bottom="3dp">

        <shape android:shape="rectangle">
            <solid android:color="#ffffff" />
        </shape>

    </item>

</layer-list>

④ 다시 dialog.xml 파일로 돌아와서 EditText에 아래의 속성을 추가한다.

android:background="@drawable/message_border"

⑤ MatchingListActivity 파일로 가서 Dialog를 보여주는 메서드를 추가하자.

private fun showDialog() {
    val dialogView = LayoutInflater.from(this).inflate(R.layout.dialog, null)
    val builder = AlertDialog.Builder(this)
        .setView(dialogView)
        .setTitle("메시지 보내기")
    val alertDialog = builder.show()
}

⑥ matchingChk 메서드의 for문에서 showDialog를 호출하자.

if(dataModel.key.toString().equals(uid)) {
    Toast.makeText(this@MatchingListActivity, "매칭된 유저입니다.", Toast.LENGTH_SHORT).show()
    showDialog()
}

⑦ 코드를 실행시켜보면, 메시지를 보내기 위한 Dialog가 잘 띄워질 것이다.

2. 쪽지 보내기

① 보내기 버튼을 클릭하면, Dialog가 닫히는 기능을 먼저 구현하도록 하겠다. showDialog 메서드에 아래의 내용을 추가한다.

val sendBtn = alertDialog.findViewById<Button>(R.id.send)
sendBtn?.setOnClickListener {
    alertDialog.dismiss()
}

② FirebaseRef 클래스의 companion object 안에 아래의 내용을 추가한다.

val userMessage = database.getReference("userMessage")

③ 메시지 전송에 사용될 data class를 정의하자. chat 디렉토리 하위로 MessageModel이라는 이름의 kotlin class를 추가한다.

data class MessageModel (
    val senderUid : String = "",
    val sendText : String = ""
)

④ 다시 MatchingListActivity로 돌아와서 receiverUid(메시지를 받을 유저의 UID) 값을 받아주자.

  • 먼저 MatchingListActivity 파일의 상단에 receiverUid를 lateinit으로 선언한다.
lateinit var receiverUid : String
  • listview.setOnItemLongClickListener에서 Long Click한 유저의 uid를 받아온다.
listview.setOnItemLongClickListener { parent, view, position, id ->
    matchingChk(myLikeUserInfo[position].uid.toString())
    receiverUid = myLikeUserInfo[position].uid.toString()
    return@setOnItemLongClickListener(true)
}

⑤ 이제 showDialog 메서드를 아래와 같이 수정한다.

val sendBtn = alertDialog.findViewById<Button>(R.id.send)
val message = alertDialog.findViewById<EditText>(R.id.message)

sendBtn?.setOnClickListener {
    val messageModel = MessageModel(uid, message!!.text.toString())
    FirebaseRef.userMessage.child(receiverUid).push().setValue(messageModel)
    alertDialog.dismiss()
}
  • push를 붙이지 않으면 가장 최근에 set된 value로 기존 값이 변경된다.
  • 기존 데이터에 append 하는 것이 목적이라면 반드시 push를 붙여야 한다.

⑥ 코드를 실행시킨 후 메시지를 보내보자. 아래와 같이 Firebase의 Realtime Database에 받는 사람의 UID > 보내는 사람의 UID와 메시지 내용이 저장될 것이다.

⑦ 그런데 누가 채팅을 보냈는지 UID로 알려주는 것은 좋은 방법이 아니다. 누가 보냈는지를 닉네임으로 나타내보기로 하자.

⑧ MainActivity의 getMyData 메서드에서 본인의 정보를 가져오기 때문에, 이 때 받아온 nickname 값을 저장해두면 될 것이다.

⑨ utils 디렉토리 하위로 MyInfo라는 이름의 kotlin class를 생성한다.

  • 나중에 변경될 값이기 때문에 반드시 var로 선언한다.
class MyInfo {
    companion object {
        var nickname : String = ""
    }
}

⑩ MainActivity의 getMyData 메서드를 아래와 같이 수정한다.

override fun onDataChange(dataSnapshot: DataSnapshot) {
    val data = dataSnapshot.getValue(UserInfo::class.java)
    currentUserGender = data?.gender.toString()
    MyInfo.nickname = data?.nickname.toString()
    getUserInfoList(currentUserGender)
}

⑪ 이제 MatchingListActivity의 showDialog 메서드에서 uid 대신 MyInfo.nickname을 사용해주면 된다.

override fun onDataChange(dataSnapshot: DataSnapshot) {
    val data = dataSnapshot.getValue(UserInfo::class.java)
    currentUserGender = data?.gender.toString()
    MyInfo.nickname = data?.nickname.toString()
    getUserInfoList(currentUserGender)
}

⑫ 마지막으로 MessageModel의 변수명도 senderNickname으로 변경해주자.

data class MessageModel (
    val senderNickname : String = "",
    val sendText : String = ""
)

⑬ 다시 코드를 실행시킨 후 메시지를 보내보자. 아래와 같이 Firebase의 Realtime Database에 받는 사람의 UID > 보내는 사람의 닉네임 메시지 내용이 저장될 것이다.

3. 메시지 확인하기

① activity_setting.xml 파일에 내가 받은 메시지를 확인할 수 있는 버튼을 추가해보자.

<Button
    android:id="@+id/myMessageList"
    android:text="받은 메시지"
    android:textStyle="bold"
    android:layout_margin="10dp"
    android:textSize="20sp"
    android:layout_width="match_parent"
    android:layout_height="60dp"/>

② 이제 메세지 목록을 보여줄 Activity를 만들어야한다. chat 디렉토리 하위로 MyMessageActivity라는 이름의 Activity를 추가한다.

③ "받은 메시지" 버튼을 클릭했을 때 MyMessageActivity로 화면이 전환될 수 있도록, SettingActivity 파일에 클릭 이벤트 리스너를 등록하자.

myMessageBtn.setOnClickListener {
    startActivity(Intent(this, MyMessageActivity::class.java))
}

④ 메시지 목록을 List View로 보여주기 위해 layout 디렉토리 하위로, message_list_view_item이라는 이름의 리소스 파일을 추가하자.

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/lvNicknameArea"
        android:text="닉네임 "
        android:textStyle="bold"
        android:textSize="20sp"
        android:layout_marginHorizontal="20dp"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
        
    <TextView
        android:id="@+id/lvMessageArea"
        android:text="메시지"
        android:textSize="20sp"
        android:layout_marginLeft="40dp"
        android:layout_marginBottom="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

⑤ 계속해서 activity_my_message.xml 파일에 ListView 태그를 추가한다.

  • ConstratinLayout의 배경색을 pink로 설정한다.
<ListView
    android:id="@+id/LVmessage"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toTopOf="parent" />

⑥ 이제 ListView를 위한 Adapter 클래스를 만들어야 한다. chat 디렉토리 하위로 MessageAdapter라는 이름의 kotlin class를 추가하자.

class MessageAdapter (val context : Context, val dataList : MutableList<MessageModel>) : 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(parent?.context).inflate(R.layout.message_list_view_item, parent, false)
        }

        val nickname = convertView!!.findViewById<TextView>(R.id.lvNicknameArea)
        nickname!!.text = dataList[position].senderNickname
        val message = convertView!!.findViewById<TextView>(R.id.lvMessageArea)
        message!!.text = dataList[position].sendText

        return convertView!!
    }
}

⑦ MyMessageActivity에 아래의 내용을 입력한다.

lateinit var listViewAdapter : MessageAdapter
val messageList = mutableListOf<MessageModel>()

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_my_message)
    val listView = findViewById<ListView>(R.id.LVmessage)
    listViewAdapter = MessageAdapter(this, messageList)
    listView.adapter = listViewAdapter
    getMyMessage()
}

private fun getMyMessage() {
    val postListener = object : ValueEventListener {
        override fun onDataChange(dataSnapshot: DataSnapshot) {
            messageList.clear()
            for (datamModel in dataSnapshot.children) {
                val message = datamModel.getValue(MessageModel::class.java)
                messageList.add(message!!)
            }
            messageList.reverse()
            listViewAdapter.notifyDataSetChanged()
        }
        
        override fun onCancelled(databseError: DatabaseError) {
            Log.w("MyMessage", "onCancelled", databseError.toException())
        }
    }
    FirebaseRef.userMessage.child(FirebaseAuthUtils.getUid()).addValueEventListener(postListener)
}
  • onDataChange 메서드에 구현함으로써, 실시간으로 메시지를 확인할 수 있게 된다.
  • messageList.clear(): 기존의 messageList를 비우고, 처음부터 다시 add하기 위해 사용된다. 데이터베이스에 변경 사항이 생길 때, 새로운 데이터만 List에 추가하는 게 아니라, 모든 데이터를 List에 넣고 있기 때문에 반드시 기존 데이터를 clear 해야 한다.
  • messageList.reverse(): 최근에 도착한 메시지일수록 상단에 표시하기 위해 사용하였다.

⑧ 코드를 실행한 후, 메시지를 보내보자. 메시지를 보낸 유저의 닉네임과 메시지 내용이 실시간으로 나타날 것이다.

4. 메시지 도착 시 push 알림 받기

① MatchingListActivity 파일 상단에 push 알림을 받을 디바이스의 토큰을 lateinit으로 선언하자.

lateinit var receiverToken : String

② Long Click 이벤트 리스너에서 클릭된 유저의 디바이스 토큰을 받아 receiverToken에 넣어주자.

listview.setOnItemLongClickListener { parent, view, position, id ->
    matchingChk(myLikeUserInfo[position].uid.toString())
    receiverUid = myLikeUserInfo[position].uid.toString()
    receiverToken = myLikeUserInfo[position].token.toString()
    return@setOnItemLongClickListener(true)
}

③ 이후 showDialog 메서드의 클릭 이벤트 리스너를 아래와 같이 수정한다.

sendBtn?.setOnClickListener {
    val messageText = message!!.text.toString()
    val messageModel = MessageModel(MyInfo.nickname, messageText)
    FirebaseRef.userMessage.child(receiverUid).push().setValue(messageModel)
    
    val noticeModel = NoticeModel(MyInfo.nickname, messageText)
    val pushModel = PushNotice(noticeModel, receiverToken)
    pushNotification(pushModel)
    
    alertDialog.dismiss()
}

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

0개의 댓글