I have recently added a chat feature to my app.
I am storing the messages and their timestamps in Firebase database.
I am trying to figure out a way of displaying (in the current chat room) only the last (let's say) 60 messages. This would mean retrieving only the last 60 items of the current conversation from Firebase using limitToLast().
Furthermore I would like to add a Load more button that would appear only when the 60 messages limit has been reached by swiping up and it should load another 60.
Is there a proper way of handling the message archive as I stated above?
Using the code below I actually retrieve the whole specific message archive. I find this ineffective when the specific chat archive has thousands of messages.
mChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
ChatMessage chatMessage = dataSnapshot.getValue(ChatMessage.class);
mMessageAdapter.add(chatMessage);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mDatabaseReference.child("chat_messages").child(chatId).addChildEventListener(mChildEventListener);
To achieve this, I recomand you using the following logic:
pageEndOffset = 0;
pageLimit = 60;
pageEndOffset += pageLimit;
Query query = mDatabaseReference.child("chat_messages")
.orderByChild("chatId").limitToFirst(pageLimit)
.startAt(pageEndOffset);
query.addValueEventListener(YourActivity.this);
Hope it helps.
Related
I'm trying to connect the Firebase real-time database as a database to my app's chats. I know that I can limit the entries that I access to last 10 entries by calling limitToLast(10) function to my Firebase reference.
The problem I have right now is that when I do this, it will limit that reference to always display the last 10 entries even if I add more entries from my app. However, I would want it to count up as I add more entries to my app. So, for instance, if I add 2 more entries, my chat list would show 12 entries and so on.
Here's my code so far with that limitToLast(10) added.
DatabaseReference tRef = database.getReference(getString(R.string.firebase_channel));
tRef.limitToLast(10).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Chat c = dataSnapshot.getValue(Chat.class);
chatAdapter.newChat(c, dataSnapshot.getKey());
Log.d(TAG, "onChildAdded\t" + c.asChat());
}
#Override
public void onChildChanged(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Chat c = dataSnapshot.getValue(Chat.class);
chatAdapter.modifyChat(c, dataSnapshot.getKey());
Log.d(TAG, "onChildChanged\t" + c.asChat());
}
#Override
public void onChildRemoved(#NonNull DataSnapshot dataSnapshot) {
Chat c = chatAdapter.removeChat(dataSnapshot.getKey());
Log.d(TAG, "onChildRemoved\t" + c.asChat());
}
#Override
public void onChildMoved(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
Chat c = dataSnapshot.getValue(Chat.class);
chatAdapter.moveChat(c, dataSnapshot.getKey());
Log.d(TAG, "onChildMoved\t" + c.asChat());
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.d(TAG, "onCancelled\t" + databaseError.toException());
Toast.makeText(context, "error loading chat list", Toast.LENGTH_SHORT).show();
}
});
What came through my mind is to actually separate the chat lists into one with which I show in my app and one with which is connected to Firebase, so when a new entry is added in my app, I'll add that to both lists. But I really don't think that using up this extra memory is the solution to this issue. Is there any way to resolve this issue otherwise?
If you only ever want to add items, how about simply ignoring onChildRemoved?
I'm developing an app that will help the user to search the books and download them which have been uploaded by other users as a form of the post.
In order to download the books first, the user should search them owing to attribute such as Title.
The posts which meet the requirement of the user should be displayed in the firebase-UI recyclerview.
This is my Firebase data structure:
I want the sample code that will create a Query which returns Id of convenient Posts such as ImgBRr2YFYfAu1WwENS1Ucj6jz13_1514813350760.
Please help me to solve the problem, any suggestions and ideas will be welcomed
Here is what you can do,
For search by language and then filter by title & author
public void search(String languangeSearchString, String title, String author){
FirebaseDatabase.getInstance().getReference().child("Posts").equalTo("language").startAt(languangeSearchString).addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String id = dataSnapshot.getKey();
if(dataSnapshot.child("title").exists() && dataSnapshot.child("author").exists()){
if(dataSnapshot.child("title").getValue(String.class).equals(title) && dataSnapshot.child("author").getValue(String.class).equals(author)){
//Here are the results
}
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
To perform searching on firebase-database, we commonly use orderBy(). Like
firebaseDatabase.getReference("Posts").child("postId")
.orderBy("authorId")
But it can't possible to use multiple orderBy() like,
firebaseDatabase.getReference("Posts").child("postId")
.orderBy("authorId")
.orderBy("title") // this will throw an error
.orderBy("language")
So you have to perform searching only on one child-node.
For more info check this Query based on multiple where clauses in Firebase
I am building a social network like Instagram. I have worked on many social networks before, using mySQL. I am new to firebase server.
I want to search users via name and want to show the follow/following button on the list view. But I am little confused to run android firebase queries in a standard format. I do not want to run unnecessary loops. Below is my database structure for the follow table.
Node name is follow and follower_id refers to the user who is following the user and user who gets followed is referred as followed_id.
How to write a simple query using android firebase to show all the users with the name starting (e.g "an") and with the status that I am already following him/her or not.
FYI: I am not using firebase rest API's.
How to write a simple query using android firebase to show all the users with the name starting (e.g "an") and with the status that I am already following him/her or not.
I think you can't do it in a single query.
You could try to do it with nested queries, something like this:
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
// Retrieve the userId
User user = dataSnapshot.getValue(User.class);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot usersFollowedDataSnapshot) {
for ( DataSnapshot userFollowedDataSnapshot : usersFollowedDataSnapshot.getChildren() ) {
// Retrieve the follower structure
Follow follow = userFollowedDataSnapshot.getValue(Follow.class);
if ( myUserId == follow.getFollowerId() ) {
// This is a user that I'm following and which name starts with "am"
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
Query secondQuery = mFirebaseDatabaseReference.child("follow").orderByChild("followedId").equalTo(user.getId());
secondQuery.addListenerForSingleValueEvent(valueEventListener);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
Query firstQuery = mFirebaseDatabaseReference.child("users").orderByChild("name").startAt("am").endAt("am\uf8ff");
firstQuery.addChildEventListener(childEventListener);
I know it's not very straightforward and looks disapponting. It could be also slow, but it worth trying.
Alternatively, you should consider to structure your database in a way to simplify the firebase queries.
See more on firebase queries for example here and here.
I am adding new data to Firebase as following code
updateChildren(childUpdates, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
}
}
And following code for ChildEventListener
fb.child(Organizations.class.getSimpleName().toLowerCase()).limitToLast(1).addChildEventListener(crudListener);
ChildEventListener crudListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Log.e(TAG,"onChildAdded "+ dataSnapshot.toString()+" \n String "+s);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
Log.e(TAG,"onChildChanged "+ dataSnapshot.toString()+" \n String "+s);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
Log.e(TAG,"onChildRemoved "+ dataSnapshot.toString());
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
Log.e(TAG,"onChildAdded "+ dataSnapshot.toString()+" \n String "+s);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
issue is when I am creating new data then onChildRemoved() triggered before onChildAdded()
I am not removing any child but onChildRemoved() triggered every time when new child is created
following Web(javascript) code it's working fine
firebase.initializeApp(config);
var commentsRef = firebase.database().ref('organizations').limitToLast(1);
commentsRef.on('child_added', function(snap) {
$('pre').text("child_added: "+JSON.stringify(snap.val(), null, 2));
});
commentsRef.on('child_changed', function(snap) {
$('pre').text("child_changed: "+JSON.stringify(snap.val(), null, 2));
});
commentsRef.on('child_removed', function(snap) {
$('pre').text("child_removed: "+JSON.stringify(snap.val(), null, 2));
});
here is my Database structure. I am creating new child under organizations
Pleaes help me how to fix this issue
You mentioned you're using limitToLast(1) when attaching crudListener. That's actually how it works.
from Quer#limitToLast() reference
The limitToLast() method is used to set a maximum number of children to be synced for a given callback. If we set a limit of 100, we will initially only receive up to 100 child_added events. If we have less than 100 messages stored in our database, a child_added event will fire for each message. However, if we have over 100 messages, we will only receive a child_added event for the last 100 ordered messages. As items change, we will receive child_removed events for each item that drops out of the active list, so that the total number stays at 100.
If you use limitToLast(1), the onChildRemoved() will be called if a new child is inserted to maintain the limit (which is 1), and then onChildAdded() called the new child.
If you don't want this behavior, remove the limitToLast(1) method
EDIT:
If you want to keep using limitToLast(1), then you have to do checking if an organization entry is really removed from the database or it's from the limit behavior.
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
dataSnapshot.getRef().addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.exists()) {
// remove this organization item from the RecyclerView
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I had similar "unexpected" behaviour connected to onChildRemoved, when I used filtering operators orderByChild("someKey").equalTo(someValue).
Being offline I added new item. When connection appeared, item I added was synced with firebase database remotely. Unexpectedly onChildRemoved was called for the added value.. When I removed the operators orderByChild("someKey").equalTo(someValue), onChildRemoved wasn't trigerred. But onChildChanged was called instead.
Hope it will save someone a few hours.
I'm building a chat app using Firebase Realtime Database. I want to implement the functionality like WhatsApp, where the user can send the message without internet (Add the message object on RecyclerView) but will have the clock icon (To show that the message was not sent yet).
When the Firebase Database receives the message (The user device connects to the internet and Firebase sends the message), I want to update the added element on the recyclerview(change the clock icon for a positive icon).
What have I tried? I have that
mFirebaseDatabaseReference = FirebaseDatabase.getInstance().getReference();
mFirebaseAdapter = new ChatFirebaseAdapter(mFirebaseDatabaseReference.child("chat_id"), user.getId());
recyclerChat.setLayoutManager(mLinearLayoutManager);
recyclerChat.setAdapter(mFirebaseAdapter);
My adapter extends FirebaseRecyclerAdapter from firebase-ui-database
I tried to add a addChildEventListener on my DatabaseReference where I get a dataSnapshot of the new element, but I don't know how to get the right position on recyclerview.
mFirebaseDatabaseReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
UPDATE RECYCLERVIEW ITEM
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I added an onCompletionListener when I add a new message, but I don't know how get the right position again.
mFirebaseDatabaseReference.push().setValue(message, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError == null) {
UPDATE RECYCLERVIEW ITEM
}
}
});
Does someone have an idea why I cant accomplish that? Thanks. I search a lot before asked, but I didn't find the right answer.
The solution to your problem lies in adding a delivered boolean which can be set to true and once you receive successful push id from your code in onCompletionListener, then all you need to do is set this boolean to that particular push id.
Now on your android client in your firebase ui you can check if this boolean is set to true and change the state of recycler view items accordingly. This solution will work because firebase database is real-time.
//setting boolean onComplete
mFirebaseDatabaseReference.push().setValue(message, new DatabaseReference.CompletionListener() { #Override public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) { if (databaseError == null) { databaseReference.child("delivered").setValue(true); } } });
For chat apps like whatsapp, messenger etc you would take another messaging route if not using firebase as backened. Which I am not discussing here as it is out of scope of asked question.