Realtime database에 ArticleModel 객체로 저장하기 위해 필요한 data class
data class ArticleModel (
val sellerId: String,
val title: String,
val createdAt: Long,
val price: String,
val imageUrl: String
){
constructor(): this("","",0,"","")
}
import android.content.Intent
import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.dldmswo1209.usedgoods.R
import com.dldmswo1209.usedgoods.databinding.FragmentHomeBinding
import com.dldmswo1209.usedgoods.mypage.DBKey.Companion.DB_ARTICLES
import com.google.android.material.snackbar.Snackbar
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.ChildEventListener
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
class HomeFragment: Fragment(R.layout.fragment_home) {
private var mBinding: FragmentHomeBinding? = null
private val binding get() = mBinding!!
private lateinit var articleAdapter: ArticleAdapter
private val auth: FirebaseAuth by lazy {
Firebase.auth
}
private val articleList = mutableListOf<ArticleModel>()
private val listener = object: ChildEventListener{
override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
val articleModel = snapshot.getValue(ArticleModel::class.java) // 데이터베이스에서 가져오는데 ArticleModel 객체로 가져옴
articleModel ?: return // null 처리
articleList.add(articleModel) // 리스트에 추가
articleAdapter.submitList(articleList)
}
override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {}
override fun onChildRemoved(snapshot: DataSnapshot) {}
override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {}
override fun onCancelled(error: DatabaseError) {}
}
private lateinit var articleDB: DatabaseReference
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mBinding = FragmentHomeBinding.bind(view)
articleList.clear() // 게시물 리스트 초기화(중복 되서 생성되는 경우를 방지)
articleDB = Firebase.database.reference.child(DB_ARTICLES) // Realtime DB reference 생성
articleAdapter = ArticleAdapter() // 어답터 생성
binding.articleRecyclerView.layoutManager = LinearLayoutManager(context)
binding.articleRecyclerView.adapter = articleAdapter // 리사이클러뷰 어답터 연결
articleDB.addChildEventListener(listener) // Realtime DB 에서 게시물 리스트를 가져옴
binding.addFloatingButton.setOnClickListener { // 게시물 작성 버튼 클릭 이벤트
// if(auth.currentUser == null){
// Snackbar.make(view, "로그인 후 사용해주세요.", Snackbar.LENGTH_LONG).show()
// return@setOnClickListener
// }
val intent = Intent(requireContext(), AddArticleActivity::class.java)
startActivity(intent)
}
}
override fun onResume() {
super.onResume()
articleAdapter.notifyDataSetChanged() // 게시물 업데이트
}
override fun onDestroy() {
super.onDestroy()
articleDB.removeEventListener(listener)
}
}
articleDB = Firebase.database.reference.child(DB_ARTICLES) 로 데이터베이스 reference 를 생성하고, articleDB.addChildEventListener(listener) 로 이벤트 리스너로 listener를 등록한다.
게시물 작성 버튼은 로그인 후에 사용 할 수 있도록 이후에 구현하도록 하겠다.
지금은 데이터베이스에 정보를 저장/불러오기가 잘 되는지 확인 하기 위해서 주석 처리를 해놓았다.
package com.dldmswo1209.usedgoods.home
import android.app.Activity
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import com.dldmswo1209.usedgoods.databinding.ActivityArticleAddBinding
import com.dldmswo1209.usedgoods.mypage.DBKey.Companion.DB_ARTICLES
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.FirebaseDatabase
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
import com.google.firebase.storage.FirebaseStorage
import com.google.firebase.storage.ktx.storage
class AddArticleActivity : AppCompatActivity() {
private var mBinding : ActivityArticleAddBinding? = null
private val binding get() = mBinding!!
private var selectedUri: Uri? = null
private val auth: FirebaseAuth by lazy {
Firebase.auth
}
private val storage: FirebaseStorage by lazy {
Firebase.storage
}
private val articleDB: DatabaseReference by lazy {
Firebase.database.reference.child(DB_ARTICLES)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = ActivityArticleAddBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.imageAddButton.setOnClickListener { // 이미지 등록 버튼 클릭 이벤트
when{
ContextCompat.checkSelfPermission(
this,
android.Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED -> {
// 권한이 부여된 경우
startContentProvider()
}
shouldShowRequestPermissionRationale(android.Manifest.permission.READ_EXTERNAL_STORAGE) -> {
// 사용자가 권한 요청을 명시적으로 거부한 경우
showPermissionContextPopup() // 팝업 요청
}
else -> {
// 그 외
requestPermissions(arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE), 1010)
}
}
}
binding.submitButton.setOnClickListener { // 게시물 등록 버튼 클릭 이벤트
val title = binding.titleEditText.text.toString()
val price = binding.priceEditText.text.toString()
val sellerId = auth.currentUser?.uid.orEmpty()
showProgress() // 지연시간 동안 progressbar 를 보여줌
// 중간에 이미지가 있으면 업로드 과정을 추가
if(selectedUri != null){
val photoUri = selectedUri ?: return@setOnClickListener
uploadPhoto(photoUri,
successHandler = { uri->
uploadArticle(sellerId, title, price, uri)
},
errorHandler = {
Toast.makeText(this, "사진 업로드에 실패했습니다.", Toast.LENGTH_SHORT).show()
hideProgress()
})
}else{
// 이미지가 없는 경우 이미지 제외하고 등록
uploadArticle(sellerId,title,price,"")
}
}
}
private fun uploadPhoto(uri: Uri, successHandler: (String) -> Unit, errorHandler: () -> Unit){
val fileName = "${System.currentTimeMillis()}.png" // 이미지 파일 이름
storage.reference.child("article/photo").child(fileName) // storage 에 article/photo/fileName 경로로 저장
.putFile(uri)
.addOnCompleteListener{
if(it.isSuccessful){
// 업로드 성공시
storage.reference.child("article/photo").child(fileName).downloadUrl
.addOnSuccessListener { uri ->
// 다운로드 성공시
successHandler(uri.toString()) // successHandler 실행
}
.addOnFailureListener {
// 다운로드 실패시
errorHandler() // errorHandler 실행
}
}else{
// 업로드 실패시
errorHandler()
}
}
}
private fun uploadArticle(sellerId: String, title: String, price: String, imageUrl: String){
val model = ArticleModel(sellerId, title, System.currentTimeMillis(), "$price 원", imageUrl) // ArticleModel 생성
articleDB.push().setValue(model) // Realtime DB에 저장
hideProgress() // progressbar 를 숨김
finish()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when(requestCode){
1010->{
if(grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED){
startContentProvider()
} else{
Toast.makeText(this, "권한을 거부하셨습니다.", Toast.LENGTH_SHORT).show()
}
}
}
}
private fun startContentProvider(){
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "image/*"
startActivityForResult(intent, 2020)
}
private fun showProgress(){
binding.progressbar.isVisible = true
}
private fun hideProgress(){
binding.progressbar.isVisible = false
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode != Activity.RESULT_OK){
return
}
when(requestCode){
2020 -> {
val uri = data?.data
if(uri != null){
binding.photoImageView.setImageURI(uri)
selectedUri = uri
} else{
Toast.makeText(this, "사진을 가져오지 못했습니다.", Toast.LENGTH_SHORT).show()
}
}else -> {
Toast.makeText(this, "사진을 가져오지 못했습니다.", Toast.LENGTH_SHORT).show()
}
}
}
private fun showPermissionContextPopup(){
AlertDialog.Builder(this)
.setTitle("권한이 필요합니다.")
.setMessage("사진을 가져오기 위해 필요합니다.")
.setPositiveButton("동의", { _,_ ->
requestPermissions(arrayOf(android.Manifest.permission.READ_EXTERNAL_STORAGE),1010)
})
.create()
.show()
}
}
이미지 등록 버튼 클릭시 권한 요청을 하는 코드와 게시물 등록 버튼 클릭시 Realtime DB에 게시물 정보를 저장하고, Storage에 image uri를 저장/불러오기를 하는 코드를 작성했다.