Chat application's "Send" button not working - java

this is my first time asking question on stackoverflow.
I'll just go straight to the point, I'm currently developing an application which involve creating a chat room using firebase, I found this
https://www.youtube.com/watch?v=uX6_w6yhj4E
and decided to follow him. Everything was going smoothly until i finish the code and tries to run it. Everything functioned as expected except the send button that was suppose to display my message in the chat room. My send button didn't respond even though i tap on it. I can't seem to find the problem.
Here is my code for the chat_room:
public class Chat_Room extends AppCompatActivity {
private Button btn_send_msg;
private EditText input_msg;
private TextView chat_conversation;
private String user_name, room_name;
private DatabaseReference root;
private String temp_key;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.chat_room);
btn_send_msg = (Button) findViewById(R.id.btn_send);
input_msg = (EditText) findViewById(R.id.msg_input);
chat_conversation = (TextView) findViewById(R.id.textview);
user_name = getIntent().getExtras().get("user_name").toString();
room_name = getIntent().getExtras().get("room_name").toString();
setTitle(" Room - "+room_name);
root = FirebaseDatabase.getInstance().getReference().child(room_name);
btn_send_msg.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
Map<String,Object> map = new HashMap<String, Object>();
temp_key = root.push().getKey();
root.updateChildren(map);
DatabaseReference message_root = root.child(temp_key);
Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("name",user_name);
map2.put("msg",input_msg.getText().toString());
//message_root.updateChildren(map2);
message_root.setValue(map2);
}
});
root.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
append_chat_conversation(dataSnapshot);
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
private String chat_msg, chat_user_name;
private void append_chat_conversation(DataSnapshot dataSnapshot){
Iterator i = dataSnapshot.getChildren().iterator();
while (i.hasNext()){
chat_msg = (String) ((DataSnapshot)i.next()).getValue();
chat_user_name = (String) ((DataSnapshot)i.next()).getValue();
chat_conversation.append(chat_user_name +" : "+chat_msg+"\n");
}
}
}
Let me know if I did something wrong or if the information provided was not enough, really appreciate for the help as I'm still a rookie in both android studio and firebase. Thanks in advance.

Welcome to stack!! :)
Update only updates data, if there is not data to update in the database then nothing will happen.
Your first update will do nothing.
Map<String,Object> map = new HashMap<String, Object>();
temp_key = root.push().getKey();
root.updateChildren(map);
The map is empty and nothing will be updated because, well, the map is empty.
The second update, will also do nothing and this is why
DatabaseReference message_root = root.child(temp_key);
Map<String, Object> map2 = new HashMap<String, Object>();
map2.put("name",user_name);
map2.put("msg",input_msg.getText().toString());
message_root.updateChildren(map2);
Here you made a reference to the child with the push key. Push keys are unique and no two push keys will ever be the same. So when you make a databaseReference to the 'message_root'. That Database reference does not exist at that point. So the attributes, "name" and "msg" do not exist in the database reference and therefore cannot be updated.
That is why you see nothing. To fix it, try this
//message_root.updateChildren(map2);
message_root.setValue(map2);
Instead of updating the message_root. Set the value instead. You have to set the value because the message_root does not exist and by calling setting value you set its existence if that makes sense.
[EDIT]
Try confirming that at least something can be posted to your database.
In your on create method create a map and push that map to your database.
If nothing is reflected, I can only assume that one of two things has happened. You have set up Firebase incorrectly(have you added your google-services json) or your security rulesare set to only alound writes from authenticated users.Make sure your security rules are open. I think the security rules are the most likely cause.
Go to the Firebase console. Go to the database and click on Rules. Make sure your rules look like this.
{
"rules": {
".read": true,
".write": true
}
}
Copy and pase those rules and post them in directly.

Related

Updating a part of the RecyclerView

i have a recycle view that contains
a description of a comment and a vote count when i press up i need the count to increase this is how it looks like at the moment
but the problem is when i press up the vote does increase but until i close that fragment and reopen it it does not update
what actually happens is when i press up vote it makes an API call which does some logic and sends it over to android which has a number of the vote for that particular item which was being clicked or upvoted
how can i update that particular record so that i can see the change without reopening the view
code:
CommentItem.java
public CommentItem(String id, String description, String voteCount)
{
this.id = id;
this.description= description;
this.voteCount = voteCount;
}
//getters and setters
CommentItemAdapter.java
...
private OnItemClickListener mlistener;
/*Interfaces*/
public interface OnItemClickListener{
void VoteUpClick(int position);
void VoteDownClick(int position);
...
}
...
CommentFragment.java
#Override
public void VoteUpClick(final int position) {
final CommentItem clickeedItem =
mitemList.get(position);
StringRequest strReq = new StringRequest(Request.Method.POST,
AppConfig.URL, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jObj = new JSONObject(response);
boolean error = jObj.getBoolean("error");
if (!error) {
String errorMsg = jObj.getString("msg");
Toast.makeText(getContext(), errorMsg,
Toast.LENGTH_SHORT).show();
} else {
String msg= jObj.getString("msg");
Toast.makeText(getContext(), msg,
Toast.LENGTH_SHORT).show();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {}
}) {
#Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<String, String>();
params.put("voteType", "up");
params.put("commentID", clickeedItem.getId().toString());
return params;
}
};
AppController.getInstance().addToRequestQueue(strReq, tag_string_req);
}
If I get it right, simply adapter.notifyDataSetChanged() will update your list. But There is a better way, using DiffUtils in recyclerView. That is, comparing old and new list, and updating only the changed item, not changing the whole data set.
Have a look at it out
https://medium.com/#iammert/using-diffutil-in-android-recyclerview-bdca8e4fbb00
Other approach, is to have the viewHolder reference from the Activity/Or Fragment, when onItemClick happens, by recyclerView.findViewHolderForAdapterPosition(position). Then change view by this view
A lot of people here had a nice advice to use DiffUtils. Thats quite a reasonable way to handle diffs inside dataset of RecycleView.
The best way to perform it right now is to use ListAdapter, which requires a DiffUtil.ItemCallback or AsyncDifferConfig. A really big pros of ListAdapter is that all differ logic is done on background, which in turns optimizes your UI.
Everything you need is to override this funs: areItemsTheSame() and areContentsTheSame(), additionally u have getChangePayload() - for detailed info about the changed item.
Don't use the notifyDataSetChanged() and other range update functions, all of that stuff is handled under-the-hood.
Yours case could be handled via different approaches. I prefer having an intermediate state which will notify user that something happens. So you can locally mark that comment with pending vote up, for example yellow arrow and when response is obtained from back-end you just need to refresh the data-list and the ItemCallback will do the diff trick for you. When response is retrieved and vote is applied it can be marked as green arrow. Those are just thoughts about the right flow.
In any case everything you need is to use the ListAdapter.sumbitList(newDataSet) and the internal differ of ListAdapter will use ItemCallback to compare old and new list.

Reading a value from a specific userid child node in database

I know this has been asked 100 times already but none of the solutions seem to be working for me.
Want to read the database of "user_preferences" for the user that is signed in (userID) and read the gender/age/weight/height values and store them in the variables shown below. currently returns null on everything (the log statement and the values). Feel like i havent got the path set up properly or something. help would be great!
and my code
mAuth = FirebaseAuth.getInstance();
mFirebaseDatabase = FirebaseDatabase.getInstance();
myRef = mFirebaseDatabase.getReference();
userID = mAuth.getCurrentUser().getUid();
DatabaseReference testRef = myRef.child("user_preferences");
testRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) { //loop through the firebase nodes
UserPreferences userPreferences = new UserPreferences();
userPreferences.setAge(ds.child(userID).getValue(UserPreferences.class).getAge());
userPreferences.setHeight(ds.child(userID).getValue(UserPreferences.class).getHeight());
userPreferences.setWeight(ds.child(userID).getValue(UserPreferences.class).getWeight());
userPreferences.setGender(ds.child(userID).getValue(UserPreferences.class).getGender());
genderSet = userPreferences.getGender();
age = userPreferences.getAge();
height = userPreferences.getHeight();
weight = userPreferences.getWeight();
Log.d(TAG, "onDataChange: " + genderSet);
//
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
These two lines of code:
DatabaseReference testRef = myRef.child("user_preferences");
testRef.addValueEventListener(...)
are effectively querying the entire node called user_preferences. That means everything at that location - all users. It sounds like this is not what you want. If you want to query just a single user, you should be specific about that in your query by adding the userID that you want to the query location:
DatabaseReference testRef = myRef.child("user_preferences").child(userID);
testRef.addValueEventListener(...)
Also, these lines of code are confusing to me:
userPreferences.setAge(ds.child(userID).getValue(UserPreferences.class).getAge());
userPreferences.setHeight(ds.child(userID).getValue(UserPreferences.class).getHeight());
userPreferences.setWeight(ds.child(userID).getValue(UserPreferences.class).getWeight());
userPreferences.setGender(ds.child(userID).getValue(UserPreferences.class).getGender());
You're deserializing a UserPreferences object for each and every field you want to populate, which is wasteful. It seems to me that you really just want to deserialize it once and remember the object:
UserPreferences userPreferences = dataSnapshot.getValue(UserPreferences.class);
Regarding the null values, you seem to be using external fields, which will not be set until the Firebase returns the network call after at least a second. Your values will be null in the meantime, so you should not be setting them onto a UI element outside of onDataChange.
Also, you have a lot of gets/sets going on, when you only need to call one getValue() for the class, then additional ones for the fields.
Then, you don't seem to want to loop over anything, so you should directly access the user node from the top reference.
For example,
mAuth = FirebaseAuth.getInstance();
mFirebaseDatabase = FirebaseDatabase.getInstance();
myRef = mFirebaseDatabase.getReference();
userID = mAuth.getCurrentUser().getUid();
DatabaseReference testRef = myRef.child("user_preferences/"+userID);
// or .child("user_preferences").child(userID)
testRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
UserPreferences userPreferences = dataSnapshot.getValue(UserPreferences.class);
Log.d(TAG, "onDataChange: " + userPreferences.getGender());
// TODO: Update some UI element here
}
#Override
public void onCancelled(DatabaseError databaseError) {
// TODO: Add error handling
}
});
}
If you only want to read the values once, use testRef.addListenerForSingleValueEvent()

Android Firebase keeps on getting old values

I'm using Firebase in my Android Project. I have a ValueEventListener and it keeps on getting the old value even the node is already modified by other devices. It seems like it is disregarding the updates made by other devices.
Here's my code:
ValueEventListener listener = destinationDatabaseReference.child(somestring)
.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue()==null)
{
}
else
{
//here I keep on getting the old value
string value = (long) dataSnapshot.child("someString").getValue();
}
}
use the query like this:-
Query query = mDatabase.getReference().child("posts").orderByChild("date").limitToLast(1);
query.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if(dataSnapshot!=null){
String notific = String.valueOf(dataSnapshot.getValue());
Log.d("key",dataSnapshot.getKey());
Log.d("title",String.valueOf(dataSnapshot.child("title").getValue()));
Log.d("content",String.valueOf(dataSnapshot.child("content").getValue()));
}
}
Thanks guys for all the help. I tried your suggestions but it did not work for me :(
However, I found this link and I would like to share just in case someone needs it.
For this kind of issue, you have to use Transaction Operations. Please see related useful links below:
Updating firebase data in several devices by Transaction Operations
Similar Question with Working Answer
Try to use a final reference like this:
URL url = new URL(yourURL);
final Firebase root = new Firebase(url.toString());
root.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) DataSnapshot dataSnapshot) {
if(dataSnapshot.getValue()==null)
{
}
else
{
//here I keep on getting the old value
string value = (long) dataSnapshot.child("someString").getValue();
}
}

How to only get child keys in Firebase

I want to display only children of location i.e Sindh and Punjab (not their children). Is it possible, and if so how can I do it?
From Best practices for data structure in the docs:
Avoid nesting data
Because the Firebase Realtime Database allows nesting data up to 32
levels deep, you might be tempted to think that this should be the
default structure. However, when you fetch data at a location in your
database, you also retrieve all of its child nodes. In addition, when
you grant someone read or write access at a node in your database, you
also grant them access to all data under that node. Therefore, in
practice, it's best to keep your data structure as flat as possible.
That is how Firebase works: If you get an item, you get its children as well. If you don't want this, you should restructure the database.
Avoid Nesting Data
Because the Firebase Realtime Database allows nesting data up to 32 levels deep, you might be tempted to think that this should be the default structure. However, when you fetch data at a location in your database, you also retrieve all of its child nodes. In addition, when you grant someone read or write access at a node in your database, you also grant them access to all data under that node. Therefore, in practice, it's best to keep your data structure as flat as possible.
Below is a structure of yours on how to implement flat database as well as how to retrieve the location key.
{
"location": {
"punjab": {
"lahore": true,
"multan": true
},
"sindh": {
"karachi": true
}
},
"place": {
"lahore": {
"location": "punjab",
"details": {
"0": "model",
"1": "steel town"
}
},
"multan": {
"location": "punjab",
"contacts": {
"0": "myarea"
}
},
"karachi": {
"location": "sindh",
"contacts": {
"0": "hadeed",
"1": "Steel town"
}
}
}
}
Below is the code that you can use to retrieve the location key.
private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();
mLocation = mDatabase.child("location");
mPlace = mDatabase.child("place");
ValueEventListener placeListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Place place = dataSnapshot.getValue(Place.class);
String location = place.location;
System.out.println(location);
}
};
mPlace.addValueEventListener(placeListener);
For more information on Firebase:
Firebase Security & Rules
DatabaseReference databaseReference = FirebaseDatabase.getInstance().getReference().child("location");
final ArrayList<String> statesArrayList= new ArrayList<>();
databaseReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
statesArrayList.add(dataSnapshot.getKey());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Yes, you can
ValueEventListener getValueListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get data from firebase
Log.d("The name of child that you need:", dataSnapshot.getKey());
// ...
}
};
databaseReference.addValueEventListener(getValueListener );
Read more: https://firebase.google.com/docs/database/android/read-and-write#listen_for_value_events
Though nesting data is not recommended in NoSQL databases like Firebase, to get the name of child nodes your code will look like this
DatabaseReference mainRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference locationref = mainRef.child("location");
final ArrayList<String> locationNames = new ArrayList<>();
locationref.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
locationNames.add(dataSnapshot.getKey());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
You can now use locationNames to do what you want.
Firebase can as quickly look up a node at level 1 as it can at level 32. Depth is not a factor that affects speed on a technical level but years of experience with Firebase has shown that deeply nested data often goes hand in hand with performance problems. As an example, i recomand you reading the offical documentation, Best practices for data structure in Firebase and Structuring your Firebase Data correctly for a Complex App.
If you don't want to change your actual database structure and assuming that location node is a direct child of your Firebase root, please see the code below:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference locationRef = rootRef.child("location");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String key = ds.getKey();
Log.d("TAG", key);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
locationRef.addListenerForSingleValueEvent(eventListener);
Your output will be:
punjab
sindh
I know this question was asked a while ago and for android, but I was looking for a solution to this and came upon this question. I couldn't use the answers provided, so here is the solution I found. This will only get the children and nothing else.
This is in javascript, but you can use whichever method is the alternative in java to make a request from a REST endpoint.
const databaseURL = "https://example.firebaseio.com" // replace value with yours
const endpoint = "location"
const user = firebase.auth().currentUser
user.getIdToken()
.then(token => {
return fetch(`${databaseURL}/${endpoint}.json?auth=${token}&shallow=true`)
})
.then(response => {
return response.json()
})
.then(json => {
console.log({json}) // contains only the children
})
.catch(error => {
console.log(error.message)
})
The important bit here is the &shallow=true otherwise you get all the data in the children.
Using curl this would be
curl 'https://example.firebaseio.com/location.json?auth=INSERT_TOKEN&shallow=true'
This assumes location is a child of the root.
More details can be had by looking at the docs
Before calling this, you have to make sure you have the currentUser available in firebase.
Also, I would heed the warnings mentioned here about proper firebase realtime database structure. However, if you are already in this spot, then this can help.
This is how u can get child names
HashMap<String, Object> allData = (HashMap<String, Object>) dataSnapshot.getValue();
String[] yourChildArray = allData.keySet().toArray(new String[0]);

howt to get Parent Value from firebase java

I'm currently new to Firebase.
I put a key in-->> user->answer->key->true
and then I used this :
Firebase re = New Firebase(blabla..user->answer);
refLoadId_Question_Question.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Map<String, Object> newPost = (Map<String, Object>) dataSnapshot.getValue();
System.out.println("Id_question1: " + dataSnapshot.getValue());
System.out.println("Id_question1: " + dataSnapshot.getChildren().toString());
list_id_question.add(dataSnapshot.getKey());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
I receive : Id_question1: {-Ja3RTtq5DMGIdVF9-pC=true} in dataSnapsho.getValue();
my Question is it Possible to get key Value from Object type in java? I want to get this value :-Ja3RTtq5DMGIdVF9-pC
anyone Please help?
Thanks..
You need to iterate over the children:
for (DataSnapshot childSnapshot: dataSnapshot.getChildren()) {
System.out.println(childSnapshot.getKey());
}
Alternatively you could also listen for children, instead of the entire answer object:
refLoadId_Question_Question.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot childSnapshot) {
System.out.println(childSnapshot.getKey());
}
}
The latter is going to be simpler when you start to deal with data synchronization instead of just just pulling down the initial data.
Say you have two people using your application:
the first user is viewing a question and (for the sake of this example) its two answers
the second user is providing a third answer to the same question
Initially the first user will download the question and the two answers. Then when the second user provide a third answer, your initial code will (likely) re-download all three answers. The second example will only be called for the new answer.
I'm Currently used this code to get the ID.
but I didn't try it if the ID question is more than one
if (dataSnapshot.getValue() != null) {
Map<String, Object> newPost = (Map<String, Object>) dataSnapshot.getValue();
String ID_Question = newPost.keySet().toString().replace("[", "");
ID_Question = ID_Question.replace("]", "");
System.out.println("Id_question1: " + ID_Question);
list_id_question.add(ID_Question);
}

Categories