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;
}
Related
I need to insert lesson object to firebase, so I put here the onData change section of code.
First of all I get data snapshot and insert the lessons that I have in firebase, after that I scan the List of Lessons and check:
if the date and time exist in the firebase in any Lesson so I do something else I insert the lesson object to firebase .
The main problem is :
when I insert the details of the lesson and press add, the lesson enter to the firebase twice minimum, and if I try another insertion the program enter to infinite loop .
will be happy for any help !
ArrayList<Lesson> existLesson=new ArrayList<>();
List<String> keys = new ArrayList<>();
int counter=0;
public void getLessons(DataSnapshot dataSnapshot){
//insert the lessons to "existLesson" arrayList
for (DataSnapshot keyNode : dataSnapshot.getChildren()) {
keys.add(keyNode.getKey());
Lesson lesson = keyNode.getValue(Lesson.class);
existLesson.add(lesson);
Log.i(tag, "data : " + lesson.getSubject());
}//for
}
int flag=1;
#Override
public void addLesson(final String subject, final String topic, final String date, final String time) {
mDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
When you call you're adding a listener to the data at. This listener will immediately read the data and call your onDataChange, and then continues to listen for updates to the data.
For each update to the data, it calls your onDataChange again. And since you're updating the data inside onDataChange, this ends in an endless loop of setValue->onDataChange->setValue->onDataChange->...
To fix this, you'd typically use addListenerForSingleValueEvent instead, as this only gets the value once and doesn't continue listening for changes.
So something like:
mDatabase.addForListenerValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
getLessons(dataSnapshot);
//Check if date and time is busy
for (Lesson lessonToCheck : existLesson) {
if (lessonToCheck.getDate().equals(date) && lessonToCheck.getTime().equals(time)) {
flag = 0;
} else {
flag = 1;
}
}//for
if (flag == 0) {
Toast.makeText(LessonDetails.this, "date exist", Toast.LENGTH_SHORT).show();
// Check empty lessons
nearestLessons(existLesson, date, time);
} else {
if (flag == 1) {
String id = mDatabase.push().getKey();
Lesson lesson = new Lesson(subject, topic, date, time, id); //create lesson
Toast.makeText(LessonDetails.this,
subject + " - " + topic + " - " + date + " - " + time, Toast.LENGTH_SHORT).show();
mDatabase.child(id).setValue(lesson);
} //add lesson to DB
} //else
Log.i(tag,"end");
} //onDataChange
})
Note that, since you're updating the data based on its current value, there's a chance that another user may be doing the same operation at almost the same time. If this can lead to conflicting updates in your use-case, consider using a transaction which combines the read and write from your code into a single (repeatable) operation.
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
I am trying to add schema where i have list of ids and value as a date. but I am getting the schema like this:
.
But I want in place of 0 is userID and date as a object. Please have a look over my code:
final String idGroup = (StaticConfig.UID + System.currentTimeMillis()).hashCode() + "";
final String currentDate = DateFormat.getDateInstance().format(new Date());
Room room = new Room();
for (String id : listIDChoose) {
AddGroupUser addGroupUser = new AddGroupUser();
addGroupUser.date = currentDate;
addGroupUser.user = id;
room.member.add(addGroupUser);
}
room.groupInfo.put("name", gName);
room.groupInfo.put("admin", StaticConfig.UID);
room.groupInfo.put("avatar",image);
FirebaseDatabase.getInstance().getReference().child("group/" + idGroup).setValue(room).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
ToastMessage("Group Created");
}
});
How to change my object so that i can get the following result. Any help will be very grateful. Thanks!!
In your Room class you have a List< AddGroupUser>. And Firebase translates a List to zero-based indexes in the JSON format.
To be able to control the key, the list needs to become a Map<String, AddGroupUser>. Then you can set the key of each AddGroupUser that you put in the map.
room.members.put(id, addGroupUser);
I have database structure like this in Firebase
I want to search a search on this structure based on key number and get the parent key in return. Meaning if i search for 8860124421 then i should get -KTEtSR7chN8te1WaW-W in return .
I am doing it like this in android -
final String number = "8860124421";
DatabaseReference global_user_entry_ref = ApplicationContext.getGlobalDataBaseReference()
.child("User-Entry-2").getRef(); //Reference to User-Entry-2
Query query = global_user_entry_ref.orderByChild("number").equalTo(number);
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
for(DataSnapshot friend: dataSnapshot.getChildren()){
String firebase_id = (String) friend.getKey();
Log.d("ContactSync","handle number "+firebase_id+" "+number+" "+friend);
}
Log.d("ContactSync","handle number outer "+dataSnapshot);
//user exist
}
else {
//user_does_not_exist
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("ContactSync","handle number oncancel "+databaseError.getMessage());
}
});
But I am not getting proper result , dataSanpshot in onDataChange looks like this -
DataSnapshot { key = User-Entry-2, value = null }
but i want to get dataSnapShot with parent of number key.
Please help , Thanks in advance
As #Frank van Puffelen stated in comments , the problem was that i was comparing a number from code with a string in the database , which does not match , Therefore the solution is to change
final String number = "8860124421";
to
final long number = 8860124421;
I've got this user class (this is a reduced version for the example, the real one has more parameters but they are implemented the same way):
public class User {
private int _userID;
private String _fullName;
public User(){
}
public User(int userID, String fullName){
this._userID = userID;
this._fullName = fullName;
}
int getUserID(){
return this._userID;
}
String getFullName(){
return this._fullName;
}
void setUserID(int userID){
this._userID = userID;
}
void setFullName(String fullName){
this._fullName = fullName;
}
}
And I want to retrieve a list from my MS SQL Server of this type of objects in Android, I'm using this method inside the connector helper class (the class in charge to make connection to the server using JDBC):
public List<User> getUsers(int ID){
java.sql.ResultSet result = null;
List<User> users = new ArrayList<User>();
User user = new User();
try {
connection = this.getConnection();
if (connection != null) {
//QUERY
String statement = "SELECT * Users WHERE groupID = "
+ ID;
Statement select = connection.createStatement();
//Calls Query
result = select.executeQuery(statement);
while (result.next()){
user.setUserID(result.getInt("UserID"));
user.setFullName(result.getString("FullName"));
System.out.println(result.getString("FullName"));
//Adds to the list
users.add(user);
}
result.close();
result = null;
closeConnection();
}
else {
System.out.println("Error: No active Connection");
}
} catch (Exception e) {
e.printStackTrace();
}
return users;
}
The data is retrieved well from the server according to the System.out.println I'm using in every iteration of the while, the problem is that the list is always filled with repeated information about the last user I retrieve, to clarify:
If I got users A, B and C, when I read user A, list has this structure:[A], when I read user B, list is:[B,B], when I read user C: [C,C,C], etc. So basically all the objects in the list are being overwritten by the last one read.
I've been struggling with this for hours, hope someone can spot the problem because I can't, thanks in advance for the help.
You instantiate a single User object before your loop, and then modify the same User object at each iteration. So you end up with N times the same User object added to your list. You must recreate a new User at each iteration:
while (result.next()){
User user = new User();
user.setUserID(result.getInt("UserID"));
...