Kotlin, FireBase 채팅어플 만들기 -1- [Splash] (tistory.com)
Kotlin, FireBase 채팅어플 만들기 -2- [Login] (tistory.com)
Kotlin, FireBase 채팅어플 만들기 -3- [Sign Up] (tistory.com)
Kotlin, FireBase 채팅어플 만들기 -4- [Bottom Navigation] (tistory.com)
Kotlin, FireBase 채팅어플 만들기 -5- [친구창_fragment] (tistory.com)
Kotlin, FireBase 채팅어플 만들기 -6- [채팅 리스트_fragment] (tistory.com)
Kotlin, FireBase 채팅어플 만들기 -7- [프로필 변경_fragment] (tistory.com)
Kotlin, FireBase 채팅어플 만들기 -8- [채팅창_activity] (tistory.com)
[전체 코드 깃허브 주소]
LasBe-code/LasbeTalk (github.com)
[참고]
[XML]
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="match_parent"
android:background="#FFFFFF"
android:clickable="false">
<ImageView
android:id="@+id/imageView4"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scaleType="centerCrop"
android:src="@drawable/loginground"
app:layout_constraintBottom_toTopOf="@id/con2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/con2"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/imageView4">
<EditText
android:id="@+id/et_registration_id"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="32dp"
android:drawableLeft="@drawable/drawable_email"
android:drawablePadding="10dp"
android:hint="Email"
android:inputType="textEmailAddress"
android:paddingLeft="8dp"
android:textColor="#000000"
android:textColorHint="#888888"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/et_registration_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/registration_iv" />
<ImageView
android:id="@+id/registration_iv"
android:layout_width="150dp"
android:layout_height="150dp"
android:layout_marginTop="32dp"
android:src="@drawable/addimage"
app:layout_constraintBottom_toTopOf="@+id/et_registration_id"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_registration_password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:drawableLeft="@drawable/drawable_password"
android:drawablePadding="10dp"
android:hint="Password"
android:inputType="textPassword"
android:paddingLeft="8dp"
android:textColor="#000000"
android:textColorHint="#888888"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/et_registration_name"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_registration_id" />
<Button
android:id="@+id/btn_registration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginBottom="50dp"
android:text="Sign Up"
android:textColor="#FFFFFF"
app:backgroundTint="#FFFF9800"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_registration_name" />
<EditText
android:id="@+id/et_registration_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginLeft="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:drawableLeft="@drawable/drawable_name"
android:drawablePadding="10dp"
android:hint="Name"
android:paddingLeft="8dp"
android:textColor="#000000"
android:textColorHint="#888888"
android:textSize="30sp"
app:layout_constraintBottom_toTopOf="@+id/btn_registration"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_registration_password" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
등록할 내용은 프로필 사진, 이메일, 비밀번호, 이름 총 4가지이다.
[액티비티 or 프래그먼트]
package com.example.lasbetalk.model
data class Friend(
val email : String? = null,
val name : String? = null,
val profileImageUrl : String? = null,
val uid : String? = null)
유저 정보를 담을 데이터 클래스이다.
private lateinit var auth: FirebaseAuth
lateinit var database: DatabaseReference
@Suppress("DEPRECATION")
class RegistrationActivity: AppCompatActivity() {
private var imageUri : Uri? = null
//이미지 등록
private val getContent =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if(result.resultCode == RESULT_OK) {
imageUri = result.data?.data //이미지 경로 원본
registration_iv.setImageURI(imageUri) //이미지 뷰를 바꿈
Log.d("이미지", "성공")
}
else{
Log.d("이미지", "실패")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_registration)
auth = Firebase.auth
database = Firebase.database.reference
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 1)
val email = et_registration_id.text
val password = et_registration_password.text
val name = findViewById<EditText>(R.id.et_registration_name).text
val button = findViewById<Button>(R.id.btn_registration)
val profile = findViewById<ImageView>(R.id.registration_iv)
var profileCheck = false
profile.setOnClickListener{
val intentImage = Intent(Intent.ACTION_PICK)
intentImage.type = MediaStore.Images.Media.CONTENT_TYPE
getContent.launch(intentImage)
profileCheck = true
}
val intent = Intent(this, LoginActivity::class.java)
button.setOnClickListener {
if(email.isEmpty() && password.isEmpty() && name.isEmpty() && profileCheck) {
Toast.makeText(this, "아이디와 비밀번호, 프로필 사진을 제대로 입력해주세요.", Toast.LENGTH_SHORT).show()
Log.d("Email", "$email, $password")
}
else{
if(!profileCheck){
Toast.makeText(this, "프로필사진을 등록해주세요.", Toast.LENGTH_SHORT).show()
} else{
auth.createUserWithEmailAndPassword(email.toString(), password.toString())
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
val user = Firebase.auth.currentUser
val userId = user?.uid
val userIdSt = userId.toString()
FirebaseStorage.getInstance()
.reference.child("userImages").child("$userIdSt/photo").putFile(imageUri!!).addOnSuccessListener {
var userProfile: Uri? = null
FirebaseStorage.getInstance().reference.child("userImages").child("$userIdSt/photo").downloadUrl
.addOnSuccessListener {
userProfile = it
Log.d("이미지 URL", "$userProfile")
val friend = Friend(email.toString(), name.toString(), userProfile.toString(), userIdSt)
database.child("users").child(userId.toString()).setValue(friend)
}
}
Toast.makeText(this, "회원가입이 완료되었습니다.", Toast.LENGTH_SHORT).show()
Log.e(TAG, "$userId")
startActivity(intent)
} else {
Toast.makeText(this, "등록에 실패했습니다.", Toast.LENGTH_SHORT).show()
}
}
}
}
}
}
public override fun onStart() {
super.onStart()
// Check if user is signed in (non-null) and update UI accordingly.
val currentUser = auth.currentUser
if(currentUser != null){
reload();
}
}
private fun reload() {
}
companion object {
private const val TAG = "EmailPassword"
}
}
profile.setOnClickListener{
val intentImage = Intent(Intent.ACTION_PICK)
intentImage.type = MediaStore.Images.Media.CONTENT_TYPE
getContent.launch(intentImage)
profileCheck = true
}
우선 프로필 이미지뷰를 클릭하면 인텐트로 MediaStore에 의해 아래와 같은 친숙한 갤러리가 나온다.
어떤 MediaStore를 사용하느냐에 따라 갤러리, 파일 선택 등 뜨는 창이 바뀌게 된다.
//사용안함
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(requestCode == PICK_FROM_ALBUM && resultCode == RESULT_OK){
imageUri = data?.data //이미지 경로 원본
registration_iv.setImageURI(imageUri) //이미지 뷰를 바꿈
}
}
원래는 주로 onActivityResult를 이용해 선택한 이미지의 Uri를 받아왔으나 버전이 변경됨에 따라
getContent로 대체되었다.
private val getContent =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
if(result.resultCode == RESULT_OK) {
imageUri = result.data?.data //이미지 경로 원본
registration_iv.setImageURI(imageUri) //이미지 뷰를 바꿈
Log.d("이미지", "성공")
}
else{
Log.d("이미지", "실패")
}
}
kotlin extension을 이용해 이미지뷰의 이미지를 변경하였는데
지원 중단된 기능이라 앞으로는 사용을 지양해야겠다.
button.setOnClickListener {
if(email.isEmpty() && password.isEmpty() && name.isEmpty() && profileCheck) {
Toast.makeText(this, "아이디와 비밀번호, 프로필 사진을 제대로 입력해주세요.", Toast.LENGTH_SHORT).show()
Log.d("Email", "$email, $password")
}
else{
if(!profileCheck){
Toast.makeText(this, "프로필사진을 등록해주세요.", Toast.LENGTH_SHORT).show()
} else{
auth.createUserWithEmailAndPassword(email.toString(), password.toString())
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
val user = Firebase.auth.currentUser
val userId = user?.uid
val userIdSt = userId.toString()
FirebaseStorage.getInstance()
.reference.child("userImages").child("$userIdSt/photo").putFile(imageUri!!).addOnSuccessListener {
var userProfile: Uri? = null
FirebaseStorage.getInstance().reference.child("userImages").child("$userIdSt/photo").downloadUrl
.addOnSuccessListener {
userProfile = it
Log.d("이미지 URL", "$userProfile")
val friend = Friend(email.toString(), name.toString(), userProfile.toString(), userIdSt)
database.child("users").child(userId.toString()).setValue(friend)
}
}
Toast.makeText(this, "회원가입이 완료되었습니다.", Toast.LENGTH_SHORT).show()
Log.e(TAG, "$userId")
startActivity(intent)
} else {
Toast.makeText(this, "등록에 실패했습니다.", Toast.LENGTH_SHORT).show()
}
}
}
auth.createUserWithEmailAndPassword을 이용해 Firebase Auth에 이메일과 패스워드를 등록시킨다.
등록이 성공적으로 완료되었다는 콜백을 받으면
FirebaseStorage.getInstance().reference.child("userImages").child("$userIdSt/photo").putFile(imageUri!!)
아까 선택한 이미지를 파이어베이스 저장소에 photo라는 이름으로 사진을 저장한다.
이미지가 성공적으로 저장되었으면 친구 창에 프로필 사진을 띄우기 위해
이미지 URL을 받아와 데이터베이스에 저장한 뒤 불러와야 하기 때문에
FirebaseStorage.getInstance().reference.child("userImages").child("$userIdSt/photo").downloadUrl
.addOnSuccessListener {
userProfile = it
Log.d("이미지 URL", "$userProfile")
val friend = Friend(email.toString(), name.toString(), userProfile.toString(), userIdSt)
database.child("users").child(userId.toString()).setValue(friend)
}
방금 저장한 사진의 url을 콜백에서 "it"으로 받아온다.
그다음 데이터 클래스를 선언하고 유저의 모든 정보를 담아준 뒤 데이터베이스에 올려준다.
[작동]
[아쉬운 점]
- 너무 많은 곳에서 참고를 하다 보니 변수명과 id가 줏대 없이 중구난방이라 가독성이 떨어진다.
- 이메일과 비밀번호, 닉네임 유효성 검사가 없다.
- 이미지 압축이 되지 않아 스토리지의 용량을 낭비한다.
'Android' 카테고리의 다른 글
Kotlin, FireBase 채팅어플 만들기 -5- [친구창_fragment] (0) | 2021.09.07 |
---|---|
Kotlin, FireBase 채팅어플 만들기 -4- [Bottom Navigation] (0) | 2021.09.04 |
Kotlin, FireBase 채팅어플 만들기 -2- [Login] (0) | 2021.09.02 |
Kotlin, FireBase 채팅어플 만들기 -1- [Splash] (0) | 2021.09.02 |
Kotlin, FireBase 채팅어플 만들기 -0- [사전설정] (0) | 2021.09.02 |
댓글