2021.07.19 - [Android/Kotlin] - [Android/Kotlin] 구글 Firebase - 1. 인증을 이용하여 로그인하기
Firebase 인증과 Firestore를 이용하여 간단한 채팅 기능을 구현해보도록 하겠다.
인증 기능은 상단에 첨부한 이전 포스트를 참고.
1. 레이아웃 만들기
1. item 만들기
Layout - New - Layout Resource File
item_message라고 만들어준다.
다음과 비슷하게 UI를 만들어 준다.
2. MainActivity
MainActivity는 다음과 같이 만들어 준다.
컴포넌트 트리에 있는 이름들은 id이므로 해당 id와 같이 바꿔주면 된다.
2. 클래스 파일
1. User.kt
class User {
constructor(){
}
constructor(email:String,name:String){
this.email=email
this.name=name
}
var email:String?=null
var profileUrl:String?=null
var name:String=""
var profiletext:String=""
var profile_text2:String=""
}
생성자를 만들고 해당 구조는 Firebase 콘솔에 만들어진다.
2. Message.kt
class Message{
constructor(){
}
constructor(message:String,email:String,imageUrl:String,name:String){
this.message=message
this.email=email // 이메일을 받는 생성자
this.imageUrl=imageUrl
this.name=name
date=Date()
}
var email:String=""
var name:String=""
var message:String=""
var date:Date=Date()
var id:String=""
var imageUrl:String=""
}
메시지에 표시될 정보는 보낸 메세지와 이름, 시간 정보가 표시된다.
imageUrl이라고 만들어 둔 것은 추후에 메시지 옆에 유저 프로필 정보를 표시할 것이기 때문에 만들어 두었는데
이는 추후 다시 포스팅할 예정.
3. MessageAdapter.kt
2021.07.23 - [Android/Kotlin] - [Android/Kotlin] 구글 Firebase - 2. Storage를 이용하여 사진, 파일 업로드하기
어댑터는 이전 포스트에서도 만들어 주었는데, 데이터와 앱 사이에서 다리 역할을 해준다고 생각하면 된다.
class MessageAdapter(var context:Context, var messageList:ArrayList<Message>): RecyclerView.Adapter<MessageAdapter.ViewHolder> (){
var itemClickListener:ItemClickListener?=null
inner class ViewHolder(itemView:View):RecyclerView.ViewHolder(itemView){
var nameTv:TextView=itemView.findViewById(R.id.name_tv)
var messageTv:TextView=itemView.findViewById(R.id.message_tv)
var timeTv:TextView=itemView.findViewById(R.id.time_tv)
fun bind(message:Message){
messageTv.text=message.message
timeTv.text=String.format("%02d:%02d",message.date.hours,message.date.minutes)
var firestore=FirebaseFirestore.getInstance()
firestore.collection("User").document(message.email).get()
.addOnSuccessListener {
var user=it.toObject(User::class.java)
nameTv.text=user?.name
}
itemView.setOnLongClickListener{
if(itemClickListener!=null){
itemClickListener?.onLongClick(message)
}
true
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
var view=LayoutInflater.from(context).inflate(R.layout.item_message,parent,false)
return ViewHolder(view)
}
override fun getItemCount(): Int {
return messageList.size
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(messageList[position])
}
interface ItemClickListener{
fun onLongClick(message:Message)
}
}
4. MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var firestore:FirebaseFirestore
lateinit var auth: FirebaseAuth
lateinit var messageEt:EditText
lateinit var submitBtn: Button
lateinit var messageList:ArrayList<Message>
lateinit var messageRv:RecyclerView
lateinit var messageAdapter: MessageAdapter
lateinit var instance:MainActivity
lateinit var logoutBtn:Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
instance=this
//채팅 메세지데이터를 관리하는 리스트 초기화
messageList=ArrayList()
//UI에 사용될 위젯 초기화
messageEt=findViewById(R.id.message_et)
submitBtn=findViewById(R.id.submit_btn)
messageRv=findViewById(R.id.list_rv)
logoutBtn=findViewById(R.id.logout_test)
//Adapter초기화
messageAdapter= MessageAdapter(this,messageList)
messageRv.adapter=messageAdapter
messageRv.layoutManager=LinearLayoutManager(this)
//Firestore 초기화
firestore= FirebaseFirestore.getInstance()
auth= FirebaseAuth.getInstance()
//Message Collection의 변경사항 리스너 등록
firestore?.collection("message")
.orderBy("date",Query.Direction.ASCENDING)
.addSnapshotListener { querySnapshot, firebaseFirestoreException ->
if (querySnapshot != null) {
for(dc in querySnapshot.documentChanges){
//Firebase에 추가된 메세지를 messageList에 추가
if(dc.type==DocumentChange.Type.ADDED){
var firebaseMessage=dc.document.toObject(Message::class.java)
firebaseMessage.id=dc.document.id
messageList.add(firebaseMessage)
messageAdapter.notifyDataSetChanged()
messageRv.scrollToPosition(messageAdapter.itemCount-1)
}
//Firebase에 삭제된 메세지를 messageList에서도 삭제
if(dc.type==DocumentChange.Type.REMOVED){
var findedMessage=messageList.filter{message-> message.id==dc.document.id}
messageList.remove(findedMessage[0])
messageAdapter.notifyDataSetChanged()
}
//Firebase에 수정된 메세지를 messageList에서도 수정
if(dc.type==DocumentChange.Type.MODIFIED){
var firebaseMessage=dc.document.toObject(Message::class.java)
var findedMessage=messageList.filter{message-> message.id==dc.document.id}
var messageIndex=messageList.indexOf(findedMessage[0])
messageList.get(messageIndex).message=firebaseMessage.message
messageAdapter.notifyDataSetChanged()
}
}
}
}
//전송 버튼을 눌렀을때
submitBtn.setOnClickListener{
onClickSubmitBtn()
}
logoutBtn.setOnClickListener {
auth.signOut()
var intent= Intent(this,LoginActivity::class.java) //로그인 페이지 이동
startActivity(intent)
this.finish()
}
//메세지를 길게 클릭했을때
messageAdapter.itemClickListener=object : MessageAdapter.ItemClickListener {
override fun onLongClick(message: Message) {
AlertDialog.Builder(instance)
.setItems(R.array.menu) { dialog, which ->
if (which == 0) {
firestore?.collection("message").document(message.id).delete()
} else if (which == 1) {
val input = EditText(instance)
AlertDialog.Builder(instance)
.setView(input)
.setPositiveButton("확인") { dialog, which ->
firestore?.collection("message").document(message.id)
.update("message", input.text.toString())
dialog.dismiss()
}
.show()
}
}
.setNegativeButton("취소") { dialog, which ->
dialog.dismiss()
}
.show()
}
}
}
fun onClickSubmitBtn(){
var msg=messageEt.text.toString()
var name=name_tv.text.toString()
if("".equals(msg)){
return
}
// 사용자가 입력한 메세지로 Message 인스턴스 생성
var message=Message(msg,auth.currentUser?.email!!,"",name)
messageEt.setText("") //메세지 입력창 초기화
firestore?.collection("message").document().set(message)
.addOnCompleteListener { task->
if(!task.isSuccessful){
Toast.makeText(this,"네트워크가 원활하지 않습니다",Toast.LENGTH_SHORT).show()
}
//else edit
}
}
}
코드가 조금 길긴 한데 찬찬히 살펴보면
1. 각 버튼들과 리스트뷰, 텍스트 요소들을 초기화 해준다.
2. Firestore에 메시지를 저장하고 그 메시지를 리스트 뷰로 가져온다.
3. 전송 버튼을 클릭하면 메시지가 리스트 뷰에 보여지게 된다.
https://firebase.google.com/docs/firestore/query-data/listen?hl=ko
onSnapshot() 메서드로 문서를 수신 대기할 수 있습니다. 사용자가 제공하는 콜백이 최초로 호출될 때 단일 문서의 현재 콘텐츠로 문서 스냅샷이 즉시 생성됩니다. 그런 다음 콘텐츠가 변경될 때마다 콜백이 호출되어 문서 스냅샷을 업데이트합니다
해당 문서는 구글 공식 문서이며 위에 설명한 2번에 해당하는 코드인 addSnapShotListener에 대해 설명되어 있다.
itemClickListener 부분은 메시지를 클릭했을 때, 메시지를 삭제 또는 수정할 수 있도록 처리한 코드이다.
결과 화면
전송 버튼을 클릭하면 메시지를 보내고
메시지를 보낸 다음 입력창을 초기화하도록 설정했다.
다음 포스트에서는 조금 더 활용해서 유저 프로필을 메시지 옆에 표시해보도록 하려 한다.
'Android > Kotlin' 카테고리의 다른 글
[Android/Kotlin] 구글 Firebase - 2. Storage를 이용하여 사진, 파일 업로드하기 (0) | 2021.07.23 |
---|---|
[Android/Kotlin] 구글 Firebase - 1. 인증을 이용하여 로그인하기 (0) | 2021.07.19 |