Duplicated item using Firebase Database childEventListener - android

i have a very simple chat app. The basic is that in Database a welcome msg is already stored as first message for that chatroom.
On START i attached a onChildAdded listener and the result is as image
The Database reference is setted in onCreate using 'GET INTENT' which is an extra String with the ID of the owner of the chatroom created in another activity. So the database is like this :
So in onCreate i got
groupChatId = Objects.requireNonNull(intent.getExtras().get("receiverId")).toString();
chatRoomReference = FirebaseDatabase.getInstance().getReference("UserChat").child(groupChatId);
In onStart i iterate using onChildAdded to retrieve the messages and put them in an ArrayList
#Override
protected void onStart() {
super.onStart();
chatRoomReference.addChildEventListener(new ChildEventListener()
I iterate like this
#Override
public void onChildAdded(#NonNull DataSnapshot dataSnapshot, #Nullable String s) {
chatText = "";
String identifier = "";
Iterator iterator = dataSnapshot.getChildren().iterator();
and finally
while (iterator.hasNext()) {
String chatBan = Objects.requireNonNull(((DataSnapshot) iterator.next()).getValue()).toString();
String chatTextFound = Objects.requireNonNull(((DataSnapshot) iterator.next()).getValue()).toString();
String chatTo = Objects.requireNonNull(((DataSnapshot) iterator.next()).getValue()).toString();
String chatUser = Objects.requireNonNull(((DataSnapshot) iterator.next()).getValue()).toString();
String chatNick = Objects.requireNonNull(((DataSnapshot) iterator.next()).getValue()).toString();
if(chatTextFound.equals(specificStringResource)
| chatTextFound.equals(otherSpecificString){
chatText = "removed";
}
chatMessage = new ChatMessage();
chatMessage.setBanId(chatBan);
chatMessage.setMessageText(chatTextFound);
chatMessage.setMessageTo(chatTo);
chatMessage.setMessageUser(chatUser);
chatMessage.setUser_nickname(chatNick);
chatMessages.add(chatMessages.size(), chatMessage);
}
displayMessages(chatMessages);
}
My displayMessages method check if the message text contains a specified string in order to exit the Activity after 3 sec
if(!chatText.equals("")){
rvMessage.setLayoutManager(new LinearLayoutManager(ChatActivity.this));
adapter = new ChatMessageAdapter(ChatActivity.this, chatMessages, chatRoomReference);
rvMessage.setAdapter(adapter);
adapter.notifyDataSetChanged();
rvMessage.smoothScrollToPosition(chatMessages.size() - 1);
try{
wait(3000);
Intent mainIntent = new Intent(ChatActivity.this,MainActivity.class);
mainIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(mainIntent);
}
});
Otherwise it should just
else {
rvMessage.setLayoutManager(new LinearLayoutManager(ChatActivity.this));
adapter = new ChatMessageAdapter(ChatActivity.this, chatMessages, chatRoomReference);
rvMessage.setAdapter(adapter);
adapter.notifyDataSetChanged();
rvMessage.smoothScrollToPosition(chatMessages.size() - 1);
}
Items in recyclerview are duplicated also when i write a new message…
My Logcat in verbose,warning etc when running app on virtual device is COMPLETELY EMPTY
I know that is quite a lot of code and a long post but as i'm still a noob in Android Studio and coding it's hard to me to be more effective in choosing what to show in order to get the solution.
Be patient and thank You anyway

Related

How to retrieve multiple data from firebase to List View in android?

i want to retrieve all this data to ListView in android studio, is that possible ?
It´s easy to do ? I´m new in code develop.
[![Firebase Data][1]][1]
I'm using "addValueEventListener" for retrieve the data:
reff2 = database.getInstance().getReference().child("Registo Inicial e Final");
reff2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange( DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
Log.e("MATRICULAA", String.valueOf(ds.child("matricula").getValue()));
String message = ds.child("nome_CONDUTOR").getValue().toString();
String message2 = ds.child("matricula").getValue().toString();
String message3 = ds.child("cod_PROJETO").getValue().toString();
String message4 = ds.child("km_INICIAL").getValue().toString();
String message5 = ds.child("km_FINAL").getValue().toString();
String message6 = ds.child("data_INICIO").getValue().toString();
String message7 = ds.child("data_FIM").getValue().toString();
String message8 = ds.child("hora_INICIO").getValue().toString();
String message9 = ds.child("hora_FIM").getValue().toString();
String message10 = ds.child("observacoes").getValue().toString();
Log.e("KM", String.valueOf(ds.child("km_FINAL").getValue()));
Nome.setText(message);
Matricula.setText(message2);
Cod.setText(message3);
Kilometros_inicial.setText(message4);
Kilometros_final.setText(message5);
Data_inicial.setText(message6);
Data_final.setText(message7);
Hora_inicial.setText(message8);
Hora_final.setText(message9);
Observacoes.setText(message10);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e("The read failed: " ,databaseError.getMessage());
}
});
This is what i get:
I want all the data from diferent nodes,i dont know why its overwriting the data..
Yes. You can.Its pretty easy. Create a class like this:
public class YourClass(){
public String cod_PROJETO;
public String data_FIM;
.... /// All or required data items here
// no argument constructor
public YourClass(){}
public String getCod_PROJETO(){
return cod_PROJETO;
}
public void setCod_PROJETO(String cod_PROJETO){
this.cod_PROJETO = cod_PROJETO;
}
.... /// Getters and setters for your data items.
.... /// Android studio code completion suggestions will make this easier;
}
Remember: if you are using getter and setter, they must be public, else the variable must be public. Using getter and setter is preferred method.
Then your this class in your valueEvent listener like this:
YourClass data = ds.getValue(YourClass.class);
Since you want to show it in ListView, use something like this in your valueEventListener:
yourClassList.clear();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
YourClass data = ds.getValue(YourClass.class);
if(data != null)
yourClassList.add(data);
}
// update your listView adapter here using notifyDataSetChanged
By this way, you can also set data in firebase like:
myRef.child("key").setValue(data); //data = object of class YourClass

Same initial object's data is being loaded in the variable. (Data is retrieved from Firebase) [duplicate]

This question already has answers here:
How to return DataSnapshot value as a result of a method?
(6 answers)
Closed 4 years ago.
So basically what I'm trying to do is 'get the names of teams from an API. Then based on the name of team, I have already stored team's country flag's image url in the firebase database. So after I get the Team name from the API, I'm trying to get the url of the following team from the database'. The problem I'm facing is 'same Url (url of first team) is being loaded to all team's url'.
JSON Code of handcoded urls of the flags:
{"Country_images_url" : {
"india a" : {
"imageURL" : "url_1"},
"india b" : {
"imageURL" : "url_2" } } }
In this for() loop I'm retrieving team's name from an API:
DatabaseReference database_country_images_url = FirebaseDatabase.getInstance().getReference("Country_images_url");
ArrayList<CricketScore> details = new ArrayList<CricketScore>;
for(int i=0, i< length; i++){
String team_1_name;
String team_2_name; // both this strings are filled with valued obtained from API
String team_1_url = url_of_team(team_1_name); // user-defined method
String team_2_url = url_of_team(team_2_name);
// And then I'm saving this data in an ArrayList
CricketScore live = new CricketScore(team_1_name, team_2_name, team_1_url, team_2_url);
details.add(live);
}
This is the user-defined method:
public String url_of_team(String team){
DatabaseReference db_url = database_country_images_url.child(team.toLowerCase()).child("imageURL");
db_url.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
url = dataSnapshot.getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return url;
}
}
Code works fine but the only prblm I'm getting is that same URL is loaded in Arraylist.
I don't understand what is going wrong. Please help me solve this.
CricketScore live = new CricketScore();
Use setter method to set values here team_1_name, team_2_name.
public String url_of_team(String team){
DatabaseReference db_url = database_country_images_url.child(team.toLowerCase()).child("imageURL");
db_url.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
url = dataSnapshot.getValue(String.class);
//Set the data in object here - team_1_url, team_2_url
details.add(live);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
return url;
}
}
Try to add data in arraylist as shown above inside the onDataChange()

Get Value from Firebase Database

I need to get the string value from the node passcode in my Firebase database to compare with a user input, but unfortunately I am not able to get the value. This is the link to my firebase database in the below image.
This is my codes below:
final DatabaseReference mDatabase = FirebaseDatabase.getInstance().getReference("pin_code");
mDatabase.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
String rface = (String) dataSnapshot.child("pincode").getValue();
if (rface.equals(userPassword) && !rface.equals("")){
Intent intent = new Intent(PinActivity.this, ProfileActivity.class);
startActivity(intent);
}
else {
if (rface.equals("") || rface.equals(null)){
// Creating new user node, which returns the unique key value
// new user node would be /users/$userid/
String userId = mDatabase.push().getKey();
// creating user object
Pin pin = new Pin(authUserId, userPassword);
mDatabase.child(userId).setValue(pin);
Intent intent = new Intent(PinActivity.this, ProfileActivity.class);
startActivity(intent);
}
else {
Toast.makeText(PinActivity.this,"Invalid PIN code", Toast.LENGTH_SHORT).show();
return;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
This is the json code
{
"pin_code" : {
"id" : "TQYTo1NHNnhPJnOxhe1Vok3U6ic2",
"pincode" : "12345"
}
}
This FirebaseDatabase.getInstance().getReference("pin_code") does not refer to the node you're looking for. Most likely you know the id property, in which case you can get the node with:
DatabaseReference collection = FirebaseDatabase.getInstance().getReference("p...");
Query query = collection.orderByChild("id").equalTo("TQT...ic2");
query.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
for (DataSnapshot child: dataSnapshot.getChildren()) {
String rface = (String) child.child("pincode").getValue();
if (rface.equals(userPassword) && !rface.equals("")){
The changes I made:
On the first line we get the collection: the node under which you want to run a query. You struck out the name of that node in the screenshot, but it's the second line you marked.
In the second line we create a query on the id property of each child node under the collection.
In the onDataChange we added a loop. This is needed because a query against the Firebase Database will potentially have multiple results. So the dataSnapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result. We loop over dataSnapshot.getChildren() to handle those multiple results.
If there can ever only be one node with the same id, you should consider changing your data structure to use the id as the key of the node. So:
pin_codes
uid1: "pincode1"
uid2: "pincode2"
Your code then becomes significantly simpler, because you don't need to query for the user anymore. You can just directly read from the path:
DatabaseReference user = FirebaseDatabase.getInstance().getReference("pin_codes").child("TQT...ic2");
user.addListenerForSingleValueEvent(new com.google.firebase.database.ValueEventListener() {
#Override
public void onDataChange(com.google.firebase.database.DataSnapshot dataSnapshot) {
String rface = (String) dataSnapshot.getValue();
if (rface.equals(userPassword) && !rface.equals("")){
Try change this:
String rface = (String) dataSnapshot.child("pincode").getValue();
To this:
String rface = (String) dataSnapshot.child("pincode").getValue(String.class);
Use the following::
Object some = dataSnapshot.getValue();
String value = some.toString();

Common time for Firebase Database

I am creating a chatting application for android. I am using Firebase Real time database for this purpose. This is how "chats" branch of database looks like :
There are unique ID's for chat rooms generated using Users unique ID's such as "513","675" etc. Inside theese chatrooms there are message objects which also have unique ID's and inside them they store information of the date message sent, name of the sender, and the text of the message. Constructor of Message object is as follows :
public Message(String text,String senderUID, Long date){
this.text = text;
this.senderUID = senderUID;
this.date = date;
}
This is how I generate Time for the each message and send them to firebase database.
sendButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
calendar = Calendar.getInstance();
String second,hour,minute;
String time;
if(calendar.get(Calendar.SECOND)<10){
second = "0"+calendar.get(Calendar.SECOND);
}
else
{
second = ""+calendar.get(Calendar.SECOND);
}
if(calendar.get(Calendar.MINUTE)<10){
minute = "0"+calendar.get(Calendar.MINUTE);
}
else
{
minute = ""+calendar.get(Calendar.MINUTE);
}
if(calendar.get(Calendar.HOUR)<10){
hour = "0"+calendar.get(Calendar.HOUR);
}
else
{
hour = ""+calendar.get(Calendar.HOUR);
}
time = date + hour + minute + second;
Log.d("time",time);
Message message = new Message(messageEditText.getText().toString(), user.getDisplayName(), Long.valueOf(time));
chatRoomDatabaseRef.child(chatID).child(user.getUid() + generateRandomNumber()).setValue(message);
messageEditText.setText("");
}
});
Here is how I get the data from database with value event listener :
chatRoomDatabaseRef.child(chatID).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Set<Message> set = new HashSet<Message>();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Message message = snapshot.getValue(Message.class);
set.add(message);
}
messageList.clear();
messageList.addAll(set);
Collections.sort(messageList, new Comparator<Message>() {
#Override
public int compare(Message o1, Message o2) {
return Long.valueOf(o1.date).compareTo(Long.valueOf(o2.date));
}
});
messageAdapter.notifyDataSetChanged();
messageListView.setSelection(messageAdapter.getCount() - 1);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
After I get the data from Firebase database I order them according to their date attribute and list them. Everything works fine but when I am filling messages' date attribute, it fills according to the local time on the phone because of that I can't sort the messages correctly. Time can differ device to device. I need to use a Time which is common and same for all the devices using my app. But I couldn't find a way.
Edit:
I still couldn't figure out but as a quick solution I created an object called sequence number in the database. I added one more attribute to the message constructor called sequence number. I read the sequence number from the database, give that number to the next message and increase the value in the database for the new messages. Then I order messages according to that number. It is not the best way to do that but it is something until I find a better way.
Try this
firebase
.database()
.ref("/.info/serverTimeOffset")
.on("value", function(offset) {
let offsetVal = offset.val() || 0;
let serverTime = Date.now() + offsetVal;
console.log("serverTime", serverTime);
});
Use as time
Message message = new Message(messageEditText.getText().toString(), user.getDisplayName(), ServerValue.TIMESTAMP);
and for retrieving it
private String getDate(long time) {
Calendar cal = Calendar.getInstance(Locale.ENGLISH);
cal.setTimeInMillis(time);
String date = DateFormat.format("dd-MM-yyyy", cal).toString();
return date;
}

Chat message with Google Firebase Realtime Database architecture with Unread Message counter and Last Message

I'm creating a chat section for android apps. By using Google Firebase.
Following task already completed
Create chat node
2. Separate particular chat thread using two users information.
3. Read all message for single chat thread.
Now my question is below.
1. How to retrieve last message by single chat thread.
2. How to create a database where i can get counter of unread message counter.
Attachment of my chat database and objective below.
Current objective.
Already done.
Firebase database architecture.
Now how it will be easier for me to create database for make solution this problem.
Firebase has it's own example project for building a Chat platform, Firechat. The project is well explained in it's doc. The data structure they used can be seen at the end of the doc. You can use their data structure. But for your case, you probably don't have any moderator, so we can make it a little simple than that.
So, you can organize it like this:
main-data
|__messageThread (all data about msgs)
| |__threadId (unique id for msg thread)
| |__chatId (unique id for chat msgs)
| |__userId
| |__userName
| |__chatMessage
| |__chatTimestamp
|
|__messageThreadMetadata
| |__threadId
| |__createdAt
| |__createdByUserId
| |__threadId
| |__threadName
| |__threadType (public/private)
| |__lastChatId (id for last chat, use this to lookup in messageThread)
|
|__users
| |__userId
| |__userId
| |__userName
| |__activeThreads (list of ids of active threads used by user)
|
|__unseenMsgCountData
| |__threadId
| |__userId
| |__unseenMsgCount
To get back to your question:
How to get last message in a thread? Please use the node lastChatId in a thread and use it to look up the respective chatMessage.
How to get unseen message count in a thread for an user? This is a data that is subjective to each thread and each user. A same thread may have two or more users, each having different unseen message count. So, you can use unseenMsgCountData where you can check across a threadId and userId. Whenever anyone posts a chat, increment the unseen msg counter by 1 in that thread for other users, who has the chat thread closed (use the activeThreads list from users to track whether he is actively in the thread or not). DO NOT increase the counter for him though, this msg is not his own unseen msg). When any other user opens that thread, reset the value to zero for him.
Hope this helps. Knock here if any further explanation or help is needed.
this is my workaround.
as you can see i am making two reference one for message and second recent is for user list.
whenever i tap on user list and open my chat page, In my chat page i m checking condition for receiver id and send status and count to recent refrence in onCreate() of chat page, like this.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
uid = pref.getString("uid", null);
sender_name = pref.getString("first_name", null);
Bundle bundle = getIntent().getExtras();
if(bundle != null && !bundle.isEmpty()){
recevier_name = bundle.getString("uname");
receiver_id = bundle.getString("UserId");
}else {
recevier_name = "Admin";
receiver_id = "1";
}
chat_date = listAdapter.SIMPLE_DATE_FORMAT.format(new Date().getTime());
if (Integer.parseInt(uid) < Integer.parseInt(receiver_id)) {
value = String.valueOf(uid + "--*--" + receiver_id);
} else {
value = String.valueOf(receiver_id+"--*--"+uid);
}
myRef1 = database.getReferenceFromUrl("https://firebaseio.com/messages/"+value);
myRef2 = database.getReferenceFromUrl("https://firebaseio.com/recents/"+value);
myRef2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
Map map1 = (Map) dataSnapshot.getValue();
if(map1 != null){
String rec_id = (String) map1.get("recever_id");
if(rec_id.equals(uid)){
myRef2.getRef().child("count").setValue("0");
myRef2.getRef().child("status").setValue("online");
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
getChat();
}
so everytime when i open my chat my status is online if receiver_id is my_id.
now in chat button click i m checking for count if status is not online, like this.
private ImageView.OnClickListener clickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
if(v==enterChatView1)
{
message_text = chatEditText1.getText().toString();
final Map<String, String> map = new HashMap<String, String>();
map.put("sender_id", uid );
map.put("recever_id", receiver_id);
map.put("sender_name", sender_name);
map.put("recevier_name", recevier_name);
map.put("text", message_text);
map.put("text_id", uid);
map.put("date", chat_date);
myRef1.push().setValue(map);
// myRef2.setValue(map);
// sendMessage(chatEditText1.getText().toString(), chat_date, UserType.OTHER);
myRef2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
Map map1 = (Map) dataSnapshot.getValue();
if(map1 != null){
String status = (String)map1.get("status");
if(map1.containsKey("count")){
if(!status.equals("online")) {
String count = (String) map1.get("count");
int val = Integer.parseInt(count);
map.put("count", String.valueOf(++val));
map.put("status", "offline");
myRef2.setValue(map);
Log.d("VALUE", "YES" + val);
}else {
map.put("count", String.valueOf(0));
map.put("status", "online");
myRef2.setValue(map);
Log.d("VALUE", "No");
}
}else {
map.put("count", String.valueOf(1));
map.put("status", "offline");
myRef2.setValue(map);
Log.d("VALUE", "No");
}
}else {
map.put("count", String.valueOf(1));
map.put("status", "offline");
myRef2.setValue(map);
Log.d("VALUE", "first time");
}
}else {
map.put("count", String.valueOf(1));
map.put("status", "offline");
myRef2.setValue(map);
Log.d("VALUE", "first time");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
chatEditText1.setText("");
}
};
and at last when you close your chat page. add status offline to recent in onBackPress() event, like this.
#Override
public void onBackPressed() {
myRef2.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
Map map1 = (Map) dataSnapshot.getValue();
if(map1!= null){
String rec_id = (String) map1.get("recever_id");
if(rec_id.equals(uid)){
myRef2.getRef().child("count").setValue("0");
myRef2.getRef().child("status").setValue("offline");
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
finish();
this.overridePendingTransition(R.anim.left_in,
R.anim.left_out);
}
#Sagar Chavada
I have to solve this problem by managing my own variable counting.
For example, there I have a msg_count variable that is watching current unread message. Think about when a user sends a message then it increasing that variable by 1
For more detail, Please check image below.

Categories

Resources