How to propery listen to child changes in FirebaseDatabase - android

I am trying to listen to my database child changes, in my case it is orders.
Below is a picture of my database, where
ODdPag... Is the uid of my customer
Lu-_1A is just .push random order naming
I can provide my code but it is confusing and not working, because i can only access Orders and not its next child.
Now I want to list all my orders and lister for changes in sub children (in order names) not uid.
I am using:
val db = FirebaseDatabase.getInstance.reference
val ref = db.child("/Orders/")
ref.addChildEventListener(object: ChildEventListener {
override fun onChildAdded(p0: DataSnapshot, p1: String?) {
ringtone.play()
itemListTable.clear()
p0.children.forEach {
it.child("order").children.forEach{ item ->
val newData = item.getValue(itemListData::class.java) ?: return
newData.itemName = item.key!!
newData.orderKey = it.key!!
itemListTable.add(newData)
}
val data = it.getValue(itemRowData::class.java) ?: return
adapter.add(itemRow(data.phoneNumber,data.time,data.locationLat,data.locationLong,data.optionalAddress,data.optionalNote,data.totalPrice,itemListTable,it.key))
}
}
override fun onChildChanged(p0: DataSnapshot, p1: String?) {
Log.d("ac1234","$p0")
p0.child("order").children.forEach{ item ->
Log.d("ac1234","1")
val newData = item.getValue(itemListData::class.java) ?: return
Log.d("ac1234","2")
newData.itemName = item.key!!
newData.orderKey = p0.key!!
itemListTable.add(newData)}
val data = p0.getValue(itemRowData::class.java) ?: return
adapter.add(itemRow(data.phoneNumber,data.time,data.locationLat,data.locationLong,data.optionalAddress,data.optionalNote,data.totalPrice,itemListTable,p0.key))
}
class itemRowData(val phoneNumber :String = "",val time :String = "",val locationLat :Double = 0.0,val locationLong :Double = 0.0,val optionalAddress :String = "",val optionalNote :String = "",val totalPrice :String = "")
class itemListData(var itemName: String = "" ,val totalQuantity: String = "",val totalPrice :Long = 0,var orderKey :String = "")
Logcat: 1 and 2 are not called
P0 shows 4 rows full of all data every time I send an order

I want all orders from all users, that is my problem. I cant reference to every ("/Orders/uid") to listen to child changes
To get all orders of all users, you should use a ValueEventListener, like in the following lines of code:
val rootRef = FirebaseDatabase.getInstance().reference
val ordersRef = rootRef.child("Orders")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (uidDataSnapshot in dataSnapshot.children) {
for (orderDataSnapshot in uidDataSnapshot.children) {
val newData = orderDataSnapshot.getValue(itemListData::class.java)
itemListTable.add(newData)
}
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
ordersRef.addValueEventListener(valueEventListener)
See, in order to get each order of each user, you should iterate twice, once to get the uidDataSnapshot and then to get the orderDataSnapshot object.

Related

Extract Data from firebase

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.

Values are not added in an array instead it keeps updating value at 0th index in Firebase Firestore

On clicking like button , it is not either increasing like count in UI nor adding a userid in likes array. It is not adding a userid into an array instead it is updating value at 0th index. I am attaching photos of logic to add values in array in firestore.
I have also added the project on github. Please take a look at it for more clearification.
https://github.com/Anshi10/Social-Media-App
Post data class which has array of user ids which liked a post.
data class Post(
val text : String = "" ,
val createdBy : user = user() ,
val createdAt : Long = 0L ,
val Likes : ArrayList<String> = ArrayList()
)
Post Dao for adding post into firestore and updating likes
class PostDao {
val db = FirebaseFirestore.getInstance()
val PostCollection = db.collection("Post")
val auth = Firebase.auth
fun addPost(text : String){
//!! is to ensure that post is created only when user is logged in otherwise it will count as illegal exception
val currentUserId = auth.currentUser!!.uid
val userdao = userDao()
GlobalScope.launch{
//getuserbyid return task which will turn into object of user data class
val Postuser = userdao.getuserbyId(currentUserId).await().toObject(user::class.java)!!
//this will give the time when the post is created
val currentTime = System.currentTimeMillis()
val post = Post(text,Postuser,currentTime)
PostCollection.document().set(post)
}
}
fun getPostById(postid : String) : Task<DocumentSnapshot>{
return PostCollection.document(postid).get()
}
fun updateLikes(postid: String) {
GlobalScope.launch {
val currentUserid = auth.currentUser!!.uid
val post = getPostById(postid).await().toObject(Post::class.java)!!
val isliked = post.Likes.contains(currentUserid)
if (isliked) {
post.Likes.remove(currentUserid)
} else {
post.Likes.add(currentUserid)
}
PostCollection.document(postid).set(post)
}
Log.d("msg","updateLikes called")
}
}
onBindViewHolder function
override fun onBindViewHolder(holder: PostViewHolder, position: Int, model: Post) {
holder.userName.text = model.createdBy.name
holder.userText.text = model.text
//with(context) load(url) into(view)
Glide.with(holder.userImage.context).load(model.createdBy.imageUrl).circleCrop().into(holder.userImage)
holder.Likecount.text = model.Likes.size.toString()
holder.userTime.text = Utils.getTimeAgo(model.createdAt)
val auth = FirebaseAuth.getInstance()
val currentuserId = auth.currentUser!!.uid
val isliked = model.Likes.contains(currentuserId)
if(isliked){
holder.likeButton.setImageDrawable(ContextCompat.getDrawable(holder.likeButton.context,R.drawable.ic_baseline_favorite_24))
}
else{
holder.likeButton.setImageDrawable(ContextCompat.getDrawable(holder.likeButton.context,R.drawable.unliked))
}
}
}
Firestore structure
first collection named post which contains field createdAt , createdBy,likesCount,text of the post.
second collection named users which contains field id , imageUrl , name

How to get specific values from all UserID (UID) and save in RecyclerViewFirebaseUI

How can I get the UID from all users from my database with the structure below and save it in my FirebaseRecyclerviewUI? I have tried for several hours but my recycler view still displays null.
-User
|
-uid
|
-advert
|
-bus_name
-category
-purl
-id
Below is my getter/setter BusinessListData class that serves as the model for receiving the values and sending them to recyclerview
class BusinessListData {
var id: String = ""
var propixurl: String = ""
var business_name: String = ""
var category: String = ""
constructor(){
}
constructor(id: String, propixurl: String, business_name: String, category: String) {
this.id = id
this.propixurl = propixurl
this.business_name = business_name
this.category = category
}
fun getID(): String{
return id
}
fun getUrl(): String{
return propixurl
}
fun getBusName(): String{
return business_name
}
fun getCate(): String{
return category
}
fun setID(id: String){
this.id = id
}
fun setUrl(propixurl: String){
this.propixurl = propixurl
}
fun setBusName(business_name: String){
this.business_name = business_name
}
fun setCate(category: String){
this.category = category
}
}
My FirebaseRecyclerViewUI details are below. The mDatabase is the reference to the database and I tried getting the snapshot of the data which was successful according to my Log.v values. The problem is that I could not get the specific values under the Advert tree in my Firebase RealTime Database
mDatabase = FirebaseDatabase.getInstance().reference
val options: FirebaseRecyclerOptions<BusinessListData> = FirebaseRecyclerOptions.Builder<BusinessListData>()
.setQuery(mDatabase, object: SnapshotParser<BusinessListData> {
override fun parseSnapshot(snapshot: DataSnapshot): BusinessListData {
Log.v("snapshotfire", snapshot.toString()) //returns all snapshot values
Log.v("snapshotfire", snapshot.child("purl").toString()) // this returns null
Log.v("snapshotfire", snapshot.child("bus_name").getValue().toString()) //this also returns null
return BusinessListData(snapshot.child("id").getValue().toString(),
snapshot.child("purl").getValue().toString(),
snapshot.child("bus_name").getValue().toString(),
snapshot.child("category").getValue().toString()
)
}
})
.build()
When you set a certain query or database reference to the FirebaseUI adapters, it will show each child node of that reference/query. Since you pass in the root of your database, your parseSnapshot will be called for each child node of the root, which does not seem to be what you want.
To show the child nodes of the User node, pass in a reference to that node:
mDatabase = FirebaseDatabase.getInstance().reference
val options: FirebaseRecyclerOptions<BusinessListData> = FirebaseRecyclerOptions.Builder<BusinessListData>()
.setQuery(mDatabase.child("User"), object: SnapshotParser<BusinessListData> {
...
Now your parseSnapshot will be called for each child node of User, and you can get the necessary keys/values from that snapshot.
For example, to get the UID you can either read the snapshot's key or its advert/uid property:
override fun parseSnapshot(snapshot: DataSnapshot): BusinessListData {
Log.v("snapshotfire", snapshot.key)
Log.v("snapshotfire", snapshot.child("advert/id").getValue())
)

Sort chat-list by the most recent message with firebase

I don't know why I got stuck in a problem that the chatList is not sorting by the last message time or by the most recent message. I have tried storing timestamp in the database and orderChildBy timestamp but it still not working. not working means the list get not sort after every message and keep showing the list as the sorted after first message.
Look at the image how chats are disordered!
This is the way I created chatList in the firebaseDatabase in ChatActiviy on sendMessage:
val timeAgo = Date().time
val myTimeMap = HashMap<String, Any?>()
myTimeMap["timestamp"] = timeAgo
myTimeMap["id"] = friendId
val friendTimeMap = HashMap<String, Any?>()
friendTimeMap["timestamp"] = timeAgo
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, I am trying to get the users details for each userswho is presented as the child of currentUser in database. (Chatlist>>CurrentUserId)
EDITED
private fun retrieveChatList() {
usersChatList = ArrayList()
val userRef = dbRef.child("ChatList").child(currentUserID).orderByChild("timestamp")
userRef.addValueEventListener(object : ValueEventListener
{
override fun onCancelled(error: DatabaseError) {
}
override fun onDataChange(snapshot: DataSnapshot)
{
(usersChatList as ArrayList<String>).clear()
if (snapshot.exists()){
for (dataSnapshot in snapshot.children){
val userUid = dataSnapshot.key
if (userUid != null) {
(usersChatList as ArrayList<String>).add(userUid)
}
}
readChatList()
}
}
})
}
private fun readChatList() {
mUsers = ArrayList()
val userRef = FirebaseFirestore.getInstance().collection("Users")
userRef.get()
.addOnSuccessListener { queryDocumentSnapshots ->
mUsers?.clear()
for (documentSnapshot in queryDocumentSnapshots) {
val user = documentSnapshot.toObject(User::class.java)
for (id in usersChatList!!){
if (user.getUid() == id){
(mUsers as ArrayList<User>).add(user)
}
}
}
retrieveGroupChatList()
chatListAdapter?.notifyDataSetChanged()
chatListAdapter = context?.let { ChatListAdapter(it, (mUsers as ArrayList<User>), true) }
recyclerViewChatList.adapter = chatListAdapter
}.addOnFailureListener { e ->
Log.d(ContentValues.TAG, "UserAdapter-retrieveUsers: ", e)
}
}
And this is the chatListAdapter for friend info
private fun friendInfo(fullName: TextView, profileImage: CircleImageView, uid: String) {
val userRef = FirebaseFirestore.getInstance().collection("Users").document(uid)
userRef.get()
.addOnSuccessListener {
if (it != null && it.exists()) {
val user = it.toObject(User::class.java)
Picasso.get().load(user?.getImage()).placeholder(R.drawable.default_pro_pic).into(profileImage)
fullName.text = user?.getFullName()
}
}
}
This is the picture of the realtime database and has a model class as ChatList, every time when I send or receive a message timestamp gets an update.
and another picture of Users in the firestore and has a model class as Users .
SOLUTION
I have a solution which works, Here i create or update a field as lastMessageTimestamp in the Firestore Users collection so the users now can sort by the lastMessageTimestamp .
val timeAgo = Date().time
val myFSMap = HashMap<String, Any?>()
myFSMap["timestamp"] = timeAgo
val friendFSMap = HashMap<String, Any?>()
friendFSMap["timestamp"] = timeAgo
//firebase chatlist references.
val chatListSenderReference = dbRef.child("ChatList").child(currentUserID).child(friendId)
val chatListReceiverReference = dbRef.child("ChatList").child(friendId).child(currentUserID)
//Firestore Users references.
val chatListSenderRef = fStore.collection("Users").document(currentUserID)
val chatListReceiverRef = fStore.collection("Users").document(friendId)
chatListSenderReference.addListenerForSingleValueEvent(object : ValueEventListener{
override fun onDataChange(p0: DataSnapshot) {
if(!p0.exists()){
chatListSenderReference.setValue(friendId)
//update the timestamp in Users collection
chatListSenderRef.update(myFSMap)
}
chatListReceiverReference.setValue(currentUserID)
chatListReceiverRef.update(friendFSMap)
override fun onCancelled(p0: DatabaseError) {
}
}
})
And at the time of reading, I use orderBy for Users
val userRef = FirebaseFirestore.getInstance().collection("Users").orderBy("lastMessageTimestamp" , Query.Direction.ASCENDING)
But It is not the complete solution because it seems like that i read and write the lastMessageTimestamp each time on messaging, which can Increase the Firebase Billing Amount to huge scary numbers. so i still need of a solution.
Simple trick is orderBy id of message. Because the id which generated by firebase base on realtime + a few factors. So let's try order by Id instead of ur timestamp. (note: just id which generated by firebase)
enter code hereSaw your post don't know if it might be useful this late hour, provided the only thing you want from firestone is the user full identity, like the name, picture etc use the userid and save the full details to android database then retrieve the identity using the Id from chatlist firebase database that matches userid
Your code might look like this
Read from chatlist firebase database
Retrieve the sender Id and time
Use the id to retrieve already added info of the person on android database
your model should contain variable for retrieve time from database
Then add all to list
After that use a comparator to sort the arraylist/list base on time
Then notify adapter change
{` ......
userDao = UserDatabase.getUserDatabase(requireContext()).userDao();
}
private void sortChatList() {
reference.child("chatlist").child(firebaseUser.getUid()).orderByChild("time").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
list.clear();;
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
String userID = Objects.requireNonNull(snapshot.child("chatid").getValue()).toString();
String time = snapshot.child("time").getValue().toString();
Chatlist chatlist = new Chatlist();
UserDB userDB = userDao.getAll(userID);
chatlist.setDate(time);
chatlist.setUserName(userDB.getUserName());
chatlist.setUserID(userID);
list.add(chatlist);
}
Collections.sort(list, new Comparator<Chatlist>() {
#Override
public int compare(Chatlist o1, Chatlist o2) {
return Integer.valueOf(o2.getTime().compareTo(o1.getTime()));
}
});
if (adapter != null) {
adapter.notifyDataSetChanged();
.........
`}

How can I sort chat list by the most recent message?

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.

Categories

Resources