본문 바로가기
Android

Kotlin, FireBase 채팅어플 만들기 -3- [Sign Up]

by LasBe 2021. 9. 2.
반응형

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)

 

[참고]

하울의 코딩 채널 - YouTube

 

GitHub - LasBe-code/LasbeTalk

Contribute to LasBe-code/LasbeTalk development by creating an account on GitHub.

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가 줏대 없이 중구난방이라 가독성이 떨어진다.
  • 이메일과 비밀번호, 닉네임 유효성 검사가 없다.
  • 이미지 압축이 되지 않아 스토리지의 용량을 낭비한다.
반응형

댓글


오픈 채팅