I have a simple to-do app in Kotlin and I want to get data from "task" node in firebase on app startup. For each child, I want to create a Todo object.
var todo = Todo("child data here")
Getting specific task
val database = FirebaseDatabase.getInstance()
val ref = database.getReference("task")
var todo = ref.child("task1").key?.let { Todo(it) }
if (todo != null) {
todoAdapter.addTodo(todo)
}
I want to get all children, there can be more than three.
If you want to get all children of a particular node, no matter how many are actually present there, then you should loop over that node using getChildren() method, as you can see in the following lines of code:
val db = FirebaseDatabase.getInstance().reference
val taskRef = db.child("task")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val value = ds.getValue(String::class.java)
Log.d("TAG", value)
//Create the desired object
var todo = Todo(value) //👈
}
}
override fun onCancelled(error: DatabaseError) {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
taskRef.addListenerForSingleValueEvent(valueEventListener)
The result in the logcat will be:
task1
task2
task3
.....
Related
I'm trying to create a like functionality in my app, the like function is already working (increment and decrement). My problem is I need to re-enter the activity again to see the new value of the like. But the value in my realtime database is already changed but in the app view, it doesn't increment or decrement (Need to re-enter to see the new value). How do i refresh a value in my app when in a button click? The code is below
//set the likes number on start
likes.text = myPlants.likes.toString();
//When like is clicked
bulbLike.setOnCheckListener(object : OnCheckListener {
override fun onChecked(view: ExpressView?) {
bulbLike(myPlants.plantId,currentUser!!.uid)
//When i remove this the values dosent change
val rootRef = FirebaseDatabase.getInstance().reference
val likeRef = rootRef.child("plants").child(myPlants.plantId).child("likes")
likeRef.get().addOnCompleteListener(OnCompleteListener<DataSnapshot?> { task ->
if (task.isSuccessful) {
val value: Long? = task.result.getValue(Long::class.java)
likes.text = value.toString()
} else {
Log.d("TAG", "Error") //Don't ignore potential errors!
}
})
}
override fun onUnChecked(view: ExpressView?) {
bulbDislike(myPlants.plantId,currentUser!!.uid)
//When i remove this the values dosent change
val rootRef = FirebaseDatabase.getInstance().reference
val likeRef = rootRef.child("plants").child(myPlants.plantId).child("likes")
likeRef.get().addOnCompleteListener(OnCompleteListener<DataSnapshot?> { task ->
if (task.isSuccessful) {
val value: Long? = task.result.getValue(Long::class.java)
likes.text = value.toString()
} else {
Log.d("TAG", "Error") //Don't ignore potential errors!
}
})
}
})
This one works it changes the value but it changes to 1 or -1
This is method or like and dislike
private fun bulbLike(plantId: String, userId: String) {
val dPlant: DatabaseReference = FirebaseDatabase.getInstance().reference
dPlant.child("plants").child(plantId).child("likes").setValue(ServerValue.increment(1))
dPlant.child("plants").child(plantId).child("userLikes").child(userId).child("status").setValue("Liked")
}
private fun bulbDislike(plantId: String, userId: String) {
val dPlant: DatabaseReference = FirebaseDatabase.getInstance().reference
dPlant.child("plants").child(plantId).child("likes").setValue(ServerValue.increment(-1))
dPlant.child("plants").child(plantId).child("userLikes").child(userId).child("status").setValue("Dislike")
}
This here gets the data once, don't use it in your case:
//THIS WONT LISTEN TO UPDATES:
likeRef.get().addOnCompleteListener(OnCompleteListener<DataSnapshot?> { task ->
if (task.isSuccessful) {
val value: Long? = task.result.getValue(Long::class.java)
likes.text = value.toString()
} else {
Log.d("TAG", "Error") //Don't ignore potential errors!
}
})
Read like this instead, using ValueEventListener, like this:
//THIS WILL LISTEN TO UPDATES DIRECTLY
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
//set likes here
val value = dataSnapshot.getValue(Long::class.java)
likes.text = value.toString()
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
}
}
likeRef.addValueEventListener(postListener)
Unable to extract information from the datasnapshot received from firebase.
Currently, I am able to get the dataSnapshot from firebase, but I am having problems extracting the information from it.
In the example below I have a lobby with the code "81MUB" and inside I have a list of players (only using one player in the example). Data from FireBase
{
"81MUB": [
{
"name": "Alejandro",
"points": 0
}
]
}
Data Class
data class Player(
val name: String,
val points: Int
)
Listener
fun getCode(): String {
val index = ('A'..'Z') + ('1'..'9')
var code = ""
for (i in 0..4){
code += index[Random().nextInt(index.size)]
}
return code
}
class MviewModel : ViewModel() {
private val _Players: MutableLiveData<MutableList<Player>> =
MutableLiveData(mutableListOf<Player>(Player("Alejandro", 0)))
private var _LobbyCode: String = ""
private val dataBase = FirebaseDatabase.getInstance()
fun getPlayer(): MutableLiveData<MutableList<Player>> = _Players
fun createLobby() {
_LobbyCode = getCode()
}
fun listener() {
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Post failed, log a message
}
}
dataBase.reference.child(_LobbyCode).addValueEventListener(postListener)
}
}
Any tips?
Each time you call getCode() you are generating a new random code. When reading data, you always use the exact same code that exists in the database. So in code, it should look like this:
val db = Firebase.database.reference
val codeRef = db.child("81MUB")
codeRef.get().addOnCompleteListener {
if (it.isSuccessful) {
val snapshot = it.result
val name = snapshot.child("name").getValue(String::class.java)
val points = snapshot.child("points").getValue(Long::class.java)
Log.d("TAG", "$name/$points")
} else {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
The result in the logcat will be:
Alejandro/0
If you however want to map the 81MUB node into an object of type Player, then your data class should look like this:
data class Player(
val name: String? = null,
val points: Int? = null
)
And in code:
val db = Firebase.database.reference
val codeRef = db.child("81MUB")
codeRef.get().addOnCompleteListener {
if (it.isSuccessful) {
val snapshot = it.result
val player = snapshot.getValue(Player::class.java)
Log.d("TAG", "${player.name}/${player.points}")
} else {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
Which will produce the exact same output as above.
You might also take into consideration, using the DatabaseReference#push() method which:
Create a reference to an auto-generated child location. The child key is generated client-side and incorporates an estimate of the server's time for sorting purposes.
Instead of using your codes.
So, my app does is a QR code scanner that adds the QR id to the current user UID. I first verify if the user already did that scan; if not, that id is added to the firebase table; otherwise, it will create that table.
This is my code:
private fun infoAdd(str2: String, view: View) {
val currentUser = auth.currentUser?.uid
val postReference = FirebaseDatabase.getInstance().getReference("organsUsers")
val dbView = postReference.child(currentUser.toString())
val postListener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val org = snapshot.child("uOrgans")
if (org != null) {
val post = org.getValue(String::class.java)
dbView.child("uOrgans").setValue("$post,$str2")
} else {
dbView.child("uOrgans").setValue(str2)
}
}
override fun onCancelled(databaseError: DatabaseError) {
}
}
dbView.addValueEventListener(postListener)
}
The str2 it's the string of the QR id.
This is what happens to Firebase:
Please Help!!!!
I first verify if the user already did that scan; if not, that id is added to the firebase table; otherwise, it will create that table.
It doesn't make any sense because in both cases you are "setting" the value of str2 into the database, that's why you see that behavior of highlighting that operation in yellow. Because a Firebase Realtime Database is a NoSQL database and is structured as pairs of keys and values, every node is a Map, which means that when using a setValue() operation, the old value is replaced with the new one.
If you want to check if the value of uOrgans exists, then update it for example, please use the following lines of code:
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val rootRef = FirebaseDatabase.getInstance().reference
val uidRef = rootRef.child("organsUsers").child(uid)
val valueEventListener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val org = snapshot.child("uOrgans")
if(org.exists()) {
val post = org.getValue(String::class.java)
dbView.getRef().updateChildren(mapOf("uOrgans" to "$post,$str2")) //Update it
Log.d("TAG", $post,$str2)
} else {
uidRef.child("uOrgans").setValue(str2)
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
uidRef.addListenerForSingleValueEvent(valueEventListener)
If doesn't exist, the str2 is added to the database.
One thing to mention, if uOrgans doesn't exist, none of its parents (organsUsers and the UID) nodes will not exist.
I don't know why I got stuck on a problem that the chatList is not sorting by the last message time or by the most recent message. I have tried by storing timestamp in the database and orderChildBy timestamp but it still not working.
This is the way how I created chatList in the firebaseDatabase
val timeAgo = Date().time
val myTimeMap = HashMap<String, Any?>()
myTimeMap["timestamp"] = timeAgo.toString()
myTimeMap["id"] = friendId
val friendTimeMap = HashMap<String, Any?>()
friendTimeMap["timestamp"] = timeAgo.toString()
friendTimeMap["id"] = currentUserID
val chatListSenderReference = dbRef.child("ChatList").child(currentUserID).child(friendId)
chatListSenderReference.keepSynced(true)
chatListSenderReference.addListenerForSingleValueEvent(object : ValueEventListener{
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(p0: DataSnapshot) {
if(!p0.exists()){
chatListSenderReference.updateChildren(friendTimeMap)
}
val chatListReceiverReference = dbRef.child("ChatList").child(friendId).child(currentUserID)
chatListReceiverReference.updateChildren(myTimeMap)
}
})
on retrieving the chatlist in recyclerView
mUsers = ArrayList()
val userRef = dbRef.child("ChatList").child(currentUserID).orderByChild("timestamp")
userRef.addValueEventListener(object : ValueEventListener
{
override fun onCancelled(error: DatabaseError) {
}
override fun onDataChange(snapshot: DataSnapshot)
{
(mUsers as ArrayList).clear()
snapshot.children.forEach {
val userUid = it.key
if (userUid != null) {
(mUsers as ArrayList).add(User(uid = userUid))
}
}
retrieveGroupChatList()
chatListAdapter?.notifyDataSetChanged()
chatListAdapter = context?.let { ChatListAdapter(it, (mUsers as ArrayList<User>), true) }
recyclerViewChatList.adapter = chatListAdapter
}
})
this is the picture of database, every time when i send or receive message timestamp get update.
You should store the timestamp of messages and then use val chatListSenderReference = dbRef.child("ChatList").child(currentUserID).orderByChild("timestamp")
Not sure if this will work for your case but for my Chat App, I had a list of the message and all I did is to sort the list
//sort method - takes a list to be sorted
private fun sortMessagesByDate(messages: MutableList<Message>) {
//sort messages by Date
messages.sortWith(compareBy { it.timestamp })
}
Method for looping through the ContactList
// method for obtaining contacts and adding them to the lists
private fun getAllContacts() {
val currentUserPhoneCredential = auth.currentUser?.phoneNumber!!
firestore.collection("Contacts")
.whereNotEqualTo("phoneNumber", currentUserPhoneCredential)
.get().addOnSuccessListener { querySnapshot ->
for (doc in querySnapshot) {
val user = doc.toObject(User::class.java)
//I have 2 lists one with just the names and another with the User Object
contactsList.add(user)
contactsNames.add(user.name!!)
}
adapter.notifyDataSetChanged()
}
Then on ListView's Adapter passing in the Names List(contactsNames)
adapter =
ArrayAdapter(requireActivity(), android.R.layout.simple_list_item_1, contactsNames)
Thereafter when a name is clicked at a specific position the app navigates to the DisplayMessage Fragment taking a UserObject corresponding to the clicked contact.
//implement listView onClick
binding.listView.setOnItemClickListener { _, _, i, _ ->
/*when a name is clicked on the ListView at a specific position the app navigates
to the DisplayMessage Fragment taking a UserObject corresponding to the
clicked contact.*/
findNavController().navigate(ChatFragmentDirections
.actionChatFragmentToMessageFragment(contactsList[i]))
}
You could also use a RecyclerView to display the names of the contacts.
I need to get from the Firebase Realtime Database node, not the entire list, but only the one that contains the value "ok".
I can get the whole list using the User model
val list:List<User> = it.children.map {it.getValue(User::class.java)}
If you need to get only the users that have the id property set to "ok", then you should use a Query line in the following lines of code:
val rootRef = FirebaseDatabase.getInstance().reference
val usersRef = rootRef.child("users")
val okQuery = usersRef.orderByChild("id").equalTo("ok")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val user = ds.getValue(User::class.java)
Log.d("TAG", ds.key + " -> " + user.id)
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore errors!
}
}
okQuery.addListenerForSingleValueEvent(valueEventListener)
The result in the logcat will be:
kolya -> ok
sasha -> ok