Check how many messages have been sent - android

I am making a messenger app, I am using firebase as my database, and I am using MVVM pattern.
I want to check if the user have new unread messages and display the number of messages near the person who sent it.
So in the MainActivity there is a RecyclerView filled with users.
This is the user model( https://ibb.co/w72K17m ).
I have a "seen" key,which means the user haven't read the message yet,in addition, I also have "reciever" which contains the current user UID. With those values I tried to do the followings:
chatRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val messageArray = ArrayList<String?>()
val usersArray = ArrayList<String?>()
for (snapshot2 in snapshot.children) {
val chat = snapshot2.getValue(Message::class.java) // Getting all the messages with values, like the photo.
if (chat!!.receiver.equals(currentUser.value!!.uid)) { // Check if the current user is the reciever
if (chat.seen == false){ // Check if the message is seen
messageArray.add(chat.message)
usersArray.add(chat.sender)
}
}
}
Log.e("Chat","messageArray: $messageArray")
Log.e("Chat","usersArray: $usersArray")
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
This is the output:
messageArray: [message1, message2 , message3, message4, message5 , message6]
usersArray: [4RDojKDSJZUOfpNky9MxuLHQJN63, 4RDojKDSJZUOfpNky9MxuLHQJN63, iromTJzrZCQVJnjcLhhdUjXi5bP2, iromTJzrZCQVJnjcLhhdUjXi5bP2, iromTJzrZCQVJnjcLhhdUjXi5bP2, iromTJzrZCQVJnjcLhhdUjXi5bP2]
I am getting all the messages and all the users who sent it to me,but I don't know how to check how many messages each ID sent.
I tried using hashMap but with no success..

I used HashMap after all, this is what I did:
fun searchNumberOfMessages(user: User){
val hasMap = HashMap<String, Any?>()
hasMap.clear()
hashMapArray.clear()
if (newMessages.value != null){
(newMessages.value as ArrayList<HashMap<String, Any?>>).clear()
}
chatRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val messageArray = ArrayList<String?>()
for (snapshot2 in snapshot.children) {
val chat = snapshot2.getValue(Message::class.java)
if (chat!!.receiver.equals(currentUser!!.uid) && chat.sender.equals(
user.uid)) {
if (chat.seen == false){
messageArray.add(chat.message)
}
}
}
hasMap["Sender"] = user
hasMap["Messages"] = messageArray.size
hashMapArray.add(hasMap)
newMessages.value = hashMapArray
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}

Related

Kotlin Firebase foreach child in path

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
.....

Infinite cycle on Firebase with Kotlin

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.

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.

How to retrieve a child from Firebase when there is a unique key Kotlin

I want to retrieve specific child values like (phonenumber, firstname, familyname) from Firebase real time database
but there is a unique key for each user
and this is the tree:
I've tried this:
var loginRef = rootRef.child("users").orderByChild("phoneNumber").equalTo(phone).addListenerForSingleValueEvent(
object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// Get data object and use the values to update the UI
val phoneNumber = dataSnapshot.getValue<User>()!!.phoneNumber
// ...
Toast.makeText(applicationContext, "phone number is: $phoneNumber", Toast.LENGTH_LONG).show()
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Data failed, log a message
Log.w(TAG, "LoginData:onCancelled", databaseError.toException())
// ...
Toast.makeText(applicationContext, "error", Toast.LENGTH_LONG).show()
}
})
and I have a simple model called User to handle the data (I know the passwords should be hashed here)
#IgnoreExtraProperties
data class User(
var firstName: String? = "",
var fatherName: String? = "",
var familyName: String? = "",
var phoneNumber: String? = "",
var password: String? = ""
) {
#Exclude
fun toMap(): Map<String, Any?> {
return mapOf(
"firstName" to firstName,
"fatherName" to fatherName,
"familyName" to familyName,
"phoneNumber" to phoneNumber,
"password" to password
)
}
}
but dataSnapshot.getValue<User>()!!.phoneNumber will never work, since the first node retrieved in this query is the unique key
what I need is something like dataSnapshot.child("unique-key/phoneNumber").value for each child i want to use, but a way easier and more efficient than making .addChildEventListener for each node
Let's firstly give some notes one the code:
first thing you need to be aware of is here:
dataSnapshot.getValue<User>()!!.phoneNumber
as it might be null if phoneNumber doesn't exist and will throw an error.
secondly, assuming you made some null handling it will still retrieve you empty string, because what you sent to model is just the unique key, and of course you can't handle it with this model.
The easiest way to solve this and get the children of retrieved node is by using for loop according to this solution: https://stackoverflow.com/a/38652274/10324295
you need to make for loop puts each item into an array list, try this code:
val userList: MutableList<User?> = ArrayList()
var loginRef = rootRef.child("users").orderByChild("phoneNumber").equalTo(phone).addListenerForSingleValueEvent(
object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
userList.clear()
for (userSnapshot in dataSnapshot.children) {
val user: User? = userSnapshot.getValue(User::class.java)
userList.add(user)
// Get Data object and use the values to update the UI
// ...
Toast.makeText(applicationContext, "hi: ${user!!.phoneNumber}", Toast.LENGTH_LONG).show()
}
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Data failed, log a message
Log.w(TAG, "LoginData:onCancelled", databaseError.toException())
// ...
Toast.makeText(applicationContext, "error", Toast.LENGTH_LONG).show()
}
})
var loginRef = rootRef.child("users").orderByChild("phoneNumber").equalTo(phone).addListenerForSingleValueEvent(
object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// retreive all children firstly by foreach
dataSnapshot.children.forEach { data ->
val userModel = data.getValue(User::class.java)
val phoneNumber = userModel!!.phoneNumber
Toast.makeText(applicationContext, "phone number is: $phoneNumber",
Toast.LENGTH_LONG).show()
}
// ...
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Data failed, log a message
Log.w(TAG, "LoginData:onCancelled", databaseError.toException())
// ...
Toast.makeText(applicationContext, "error",
Toast.LENGTH_LONG).show()
}
})

Categories

Resources