Firebase Chat

MainActivity

import android.graphics.drawable.BitmapDrawable
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.ktx.auth
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DatabaseReference
import com.google.firebase.database.ValueEventListener
import com.google.firebase.database.ktx.database
import com.google.firebase.ktx.Firebase
import com.neco_desarrollo.fbchat.databinding.ActivityMainBinding
import com.squareup.picasso.Picasso
import java.util.ArrayList

class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
lateinit var auth: FirebaseAuth
lateinit var adapter: UserAdapter

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
auth = Firebase.auth
setUpActionBar()
val database = Firebase.database
val myRef = database.getReference("messages")
binding.bSend.setOnClickListener {
myRef.child(myRef.push().key ?: "blabla").setValue(User(auth.currentUser?.displayName, binding.edMessage.text.toString()))
}
onChangeListener(myRef)
initRcView()

}

private fun initRcView() = with(binding){
adapter = UserAdapter()
rcView.layoutManager = LinearLayoutManager(this@MainActivity)
rcView.adapter = adapter
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.main_menu, menu)
return super.onCreateOptionsMenu(menu)
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(item.itemId == R.id.sign_out){
auth.signOut()
finish()
}
return super.onOptionsItemSelected(item)
}

private fun onChangeListener(dRef: DatabaseReference){
dRef.addValueEventListener(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
val list = ArrayList<User>()
for(s in snapshot.children){
val user = s.getValue(User::class.java)
if(user != null)list.add(user)
}
adapter.submitList(list)
}

override fun onCancelled(error: DatabaseError) {

}

})
}

private fun setUpActionBar(){
val ab = supportActionBar
Thread{
val bMap = Picasso.get().load(auth.currentUser?.photoUrl).get()
val dIcon = BitmapDrawable(resources, bMap)
runOnUiThread {
ab?.setDisplayHomeAsUpEnabled(true)
ab?.setHomeAsUpIndicator(dIcon)
ab?.title = auth.currentUser?.displayName
}
}.start()

}
}

SignInActivity

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import com.google.android.gms.auth.api.signin.GoogleSignIn
import com.google.android.gms.auth.api.signin.GoogleSignInClient
import com.google.android.gms.auth.api.signin.GoogleSignInOptions
import com.google.android.gms.common.api.ApiException
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.GoogleAuthProvider
import com.google.firebase.auth.ktx.auth
import com.google.firebase.ktx.Firebase
import com.neco_desarrollo.fbchat.databinding.ActivitySignInBinding

class SignInAct : AppCompatActivity() {
lateinit var launcher: ActivityResultLauncher<Intent>
lateinit var auth: FirebaseAuth
lateinit var binding: ActivitySignInBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySignInBinding.inflate(layoutInflater)
setContentView(binding.root)
auth = Firebase.auth
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){
val task = GoogleSignIn.getSignedInAccountFromIntent(it.data)
try {
val account = task.getResult(ApiException::class.java)
if(account != null){
firebaseAuthWithGoogle(account.idToken!!)
}
} catch (e: ApiException){
Log.d("MyLog","Api exception")
}
}
binding.bSignIn.setOnClickListener {
signInWithGoogle()
}
checkAuthState()
}

private fun getClient(): GoogleSignInClient{
val gso = GoogleSignInOptions
.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id))
.requestEmail()
.build()
return GoogleSignIn.getClient(this, gso)
}

private fun signInWithGoogle(){
val signInClient = getClient()
launcher.launch(signInClient.signInIntent)
}

private fun firebaseAuthWithGoogle(idToken: String){
val credential = GoogleAuthProvider.getCredential(idToken, null)
auth.signInWithCredential(credential).addOnCompleteListener {
if(it.isSuccessful){
Log.d("MyLog","Google signIn done")
checkAuthState()
} else {
Log.d("MyLog","Google signIn error")
}
}
}

private fun checkAuthState(){
if(auth.currentUser != null){
val i = Intent(this, MainActivity::class.java)
startActivity(i)
}
}

}

User

data class User(
val name: String? = null,
val message: String? = null
)

UserAdapter

import android.view.LayoutInflater
import android.view.ViewGroup

import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.neco_desarrollo.fbchat.databinding.UserListItemBinding

class UserAdapter : ListAdapter<User, UserAdapter.ItemHolder>(ItemComparator()) {

class ItemHolder(private val binding: UserListItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(user: User) = with(binding){
message.text = user.message
userName.text = user.name
}
companion object{
fun create(parent: ViewGroup): ItemHolder{
return ItemHolder(UserListItemBinding
.inflate(LayoutInflater.from(parent.context), parent, false))
}
}
}

class ItemComparator : DiffUtil.ItemCallback<User>(){
override fun areItemsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}

override fun areContentsTheSame(oldItem: User, newItem: User): Boolean {
return oldItem == newItem
}

}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ItemHolder {
return ItemHolder.create(parent)
}

override fun onBindViewHolder(holder: ItemHolder, position: Int) {
holder.bind(getItem(position))
}
}

activity_main.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"
tools:context=".MainActivity">

<Button
android:id="@+id/bSend"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:text="Send"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<EditText
android:id="@+id/edMessage"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="Message"
android:inputType="textPersonName"
app:layout_constraintBottom_toTopOf="@+id/bSend"
app:layout_constraintEnd_toEndOf="@+id/bSend"
app:layout_constraintStart_toStartOf="@+id/bSend" />

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/edMessage"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="1.0" />

</androidx.constraintlayout.widget.ConstraintLayout>

activity_sign_in.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"
tools:context=".SignInAct">

<Button
android:id="@+id/bSignIn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="google signin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

user_list_item.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"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<androidx.cardview.widget.CardView
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:layout_marginTop="5dp"
android:layout_marginEnd="5dp"
android:backgroundTint="#C9FACB"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">

<TextView
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textColor="@color/black" />

<TextView
android:id="@+id/userName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView"
android:textSize="12sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

main_menu.xml

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

<item
android:title="Sign Out"
android:id="@+id/sign_out"/>
</menu>

6 комментариев для “Firebase Chat”

  1. Сергей, привет!
    спасибо за крутые уроки!
    подскажи, пожалуйста, в чем может быть ошибка, если в БД ФБ сообщения появляются, а на экране смартфона нет(показано только первое сообщение).

    1. Для тех кто так же столкнулся с подобной ошибкой:
      В user_list_item.xml в атрибутах ConstraintLayout layout_height нужно поменять с match_parent на wrap_content, так он не будет занимать весь RecyclerView

  2. Если вдруг кто то читает то столкнулся с проблемой что приложение просто не запускается без ошибок каких либо все прекрасно работало до 4 урока смотрел код идентичный даже отсюда скопировал но без изменений хотелось бы узнать причину почему не работает

  3. Скиньте пожалуйста ссылку на готовый проект, и что поменять для firebase? Вы конечно уберите свои данные от firebase

  4. Подскажите пожалуйста. В БД. сообщения появляются, а на экране эмулятора не отображаются. Где может быть проблема?
    ConstraintLayout layout_height = wrap_content

Добавить комментарий для Сергей Отменить ответ

Ваш адрес email не будет опубликован.