Im creating an app with Firebase database here is my data structure:
{
"teachers" : {
"USYSacnOjDR5EAPwljZMHtggN9I2" : {
"name" : {
"teacher_name" : "Alison"
},
"members": {
"sIrfMA3bm0R9uj55nDUFpFyabcL2" : true
}
}
},
"users" : {
"sIrfMA3bm0R9uj55nDUFpFyabcL2" : {
"position" : {
"bpos" : "0"
},
"name" : {
"email" : "m#m.com",
"name" : "John",
"surname" : "Clash"
}
}
So now in RecyclerView im trying to display all of the Alison teacher members, as you can see John Clash is set to true. Anyway when i try to retrieve user this way:
reference = FirebaseDatabase.getInstance().getReference("teachers").child(teacherkey).child("members");
reference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
User user = dataSnapshot.child("name").getValue(User.class);
result.add(user);
adapter.notifyDataSetChanged();
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
User user = dataSnapshot.child("name").getValue(User.class);
result.remove(user);
adapter.notifyDataSetChanged();
}
the app crash because of NPE, datasnapshot key is valid, it is John's account key but value of snapshot is true. How can i solve that and get from John's data name and surname?
#Edit:
Here is my retrieving code:
public void updateList() {
reference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String userId = ds.getKey();
DatabaseReference userIdRef = FirebaseDatabase.getInstance().getReference().child("users").child(userId);
userIdRef.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
User user = dataSnapshot.child("name").getValue(User.class);
result.add(user);
adapter.notifyDataSetChanged();
}
#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) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
To solve this, you need to query your database twice. Please see the code below:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference membersRef = rootRef.child(teachers).child(teacherId).child("members");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String userId = ds.key();
DatabaseReference userIdRef = rootRef.child("users").child(userId);
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
Log.d("TAG", user.getName());
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
userIdRef.addListenerForSingleValueEvent(eventListener);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
membersRef.addListenerForSingleValueEvent(valueEventListener);
You have 2 approaches to solve this issue:
a.) Inside onChildAdded you already have, if the value you got is true, do another search in database, but this time in "users" node. Something like
newReference = FirebaseDatabase.getInstance().getReference("users").child(dataSnapshot.getKey).child("name")
b.) In the "members" node, write "John Clash" instead of "true". This approach will work for now, but you will fall into the first approach if someday you need to retrieve email address as well
Related
Not sure why, but with the code I have, I cannot seem to get the value of 'isOnline':
dolRef = DatabaseReference dolRef = FirebaseDatabase.getInstance().getReference("DriversOnline");
dolRef = dolRef.child("iosDriver");
dolRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String driverid = ds.getKey();
// get value of 'isOnline'
dolRef = dolRef.child(driverid);
Log.e(TAG, "dolRef: " + dolRef);
dolRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot dss : dataSnapshot.getChildren()) {
String online = dss.child("isOnline").getValue(String.class);
Log.e(TAG, "Online: " + online);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Below is a part of my firebase db:
"DriversOnline" : {
"iosDriver" : {
"BruEGfToc8axIWJk1o01fxcwd8I2" : { // driverId
"isOnline" : "true",
"latitude" : 45.276,
"longitude" : -66.064
}
}
}
Any idea why I can't get the value of 'isOnline' other than null ?
I think you're nesting your listeners one level deeper than needed.
DatabaseReference iosRef = FirebaseDatabase.getInstance().getReference("DriversOnline/iosDriver");
iosRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot driverSnapshot: dataSnapshot.getChildren()) {
String driverid = driverSnapshot.getKey();
DataSnapshot isOnlineSnapshot = driverSnapshot.child("isOnline");
System.out.println(isOnlineSnapshot.getValue(String.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
});
Some things to note:
Reassigning one generically named dolRef variable 4 times in a block of code like this is a code-smell. It makes it much harder to follow what's going on, and to check if the variable points to the right location. Give each of them a name that clearly indicates what they point to, as I've done above.
There is no need to attach a second listener, as the value of isOnline is right in the driverSnapshot. You can just request the child snapshot with the right name, and then the value from that.
Don't ignore error, as that hides potential problems. At the very least throw them, as I've done above.
driverid = FirebaseDatabase.getInstance().getReference("driver");//If there is another driver table, the path must be two layers.
driverid.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Driver driver = dataSnapshot.getValue(Driver.class);//class model
dolRef = FirebaseDatabase.getInstance().getReference("DriversOnline/iosDriver").child(driver.getDriverID);
dolRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
DriversOnline driversOnline= dataSnapshot.getValue(DriversOnline .class);//class model
log.d("driverid","isOnline :"+driversOnline.getisOnline)
//display -> isOnline : true
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
{
"History" : {
"-LIRZ4Nf2HyTTYtZEeOA" : {
"destination" : "101 Main Street, Your Town, Your Country",
"driver" : "vP9r4F2yDWRRuvKjRiQvMEXVuoK2",
"payment response" : "approved",
"rating" : 0,
"ridePrice" : 3.63,
"rider" : "C0RjB5NPZcTvWz9XiUAhpTDOK0C2",
"status" : "accepted",
"timestamp" : 1532709012
}
}
The above is the History node in my Firebase database.
My ultimate goal is to send an alert dialog once the payment response = "approved" but, in this case, when there is more than one key in the History node, the alerts run on top of each other.
What can I do so this does not happen?
The code I am using to get the key for each History entry is:
private void getPaymentResponse() {
final DatabaseReference paymentConfirmed = FirebaseDatabase.getInstance().getReference("History");
paymentConfirmed.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
ryydeKey = ds.getKey(); **<-- gets the key**
DatabaseReference response = FirebaseDatabase.getInstance().getReference("History")
.child(ryydeKey);
response.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
if (child.getKey().equals("payment response")) {
String response = String.valueOf(child.getValue().toString());
if (response.equals("approved")) {
proceedToPickupDialog();
}
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
I am assuming that the code:
for(DataSnapshot ds : dataSnapshot.getChildren()) {
ryydeKey = ds.getKey();
is getting all the keys, but all I want is the present key.
Edit
I am putting in this code and nothing is printing up. Tried logs and toasts.
DatabaseReference payRef = FirebaseDatabase.getInstance().getReference();
Query query = payRef.child("History").orderByChild("driver").equalTo(driverId).limitToLast(1);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String response = ds.child("payment response").getValue(String.class);
Log.e(TAG, "response = " + response);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
query.addListenerForSingleValueEvent(valueEventListener);
When you are using getChildren() method, it means that you are looping through the entire DataSnapshot object. If the DataSnapshot contains more then one child, then the alerts will run on top of each other. If you want to check only for a particular child, you need to change the code a little bit.
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference idRef = rootRef.child("History").child("-LIRZ4Nf2HyTTYtZEeOA");
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String response = dataSnapshot.child("payment response").getValue(String.class);
if(response.equalTo("approved")) {
Log.d("TAG", "Payment approved!");
} else {
Log.d("TAG", "Payment not approved!");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
idRef.addListenerForSingleValueEvent(valueEventListener);
Using this code, you get only the response form a single child. To actually get this work done, first you need to store this value -LIRZ4Nf2HyTTYtZEeOA in a variable in order to be able to use in your reference.
Edit: This another approach using a query"
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
Query query = rootRef.child("History").orderByChild("driver").equalsTo(driverId).limitToLast(1);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
String response = dataSnapshot.child("payment response").getValue(String.class);
if(response.equalTo("approved")) {
Log.d("TAG", "Payment approved!");
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
query.addListenerForSingleValueEvent(valueEventListener);
public void getFirstName(View view) {
Query newQuery = database.getReference("users").orderByChild("firstName");
newQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Iterable<DataSnapshot> iterator = dataSnapshot.getChildren();
for (DataSnapshot child : iterator) {
Friends friends = child.getValue(Friends.class);
Toast.makeText(DBActivity.this, friends.firstName, Toast.LENGTH_SHORT).show();
}
}
#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 am trying to retrieve the first names of all friends. When I try the above code for the mentioned JSON file structure, I am getting this error "Can't convert object of type java.lang.String to type com.example.seth.photopicker.Friends". Please help me with this issue.
Try this:
Query newQuery = database.getReference("users");
newQuery.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
String fname=dataSnapshot.child("firstName").getValue().toString();
Toast.makeText(DBActivity.this, friends.firstName, Toast.LENGTH_SHORT).show();
}
}
The datasnapshot is at users and since you are using onChildAdded you do not need to iterate to be able to get the first name.
Query newQuery = database.getReference("users").orderByChild("firstName");
newQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
Friends friends = dataSnapshot1.getValue(Friends.class);
Toast.makeText(DBActivity.this, friends.firstName, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Equivalent code for above answer without using Child Listener.
I want to retrieve "7753400075_Canada_twinpalms_||_twinpalms_20170824001545_F5BA7F33AD51CE283332001BEC409A46" key, How to do so? Here is my code...
ref.addValueEventListener(new ValueEventListener() { //addListenerForSingleValueEvent
#Override
public void onCancelled(DatabaseError arg0) {
}
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Object response = dataSnapshot.getValue();
System.out.println("read data :"+response);
}
});
ref.child("chatmessage").child("devicetoken").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
// Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());
// System.out.println("previousChildName :"+previousChildName);
}
}
});
Here is the Firebase database example:
I tried using child data snapshot method but did not worked. It returns all data. But i need to get only the new child added parent. Any help for this will be much appreciated.
To solve this, you need to filter your results. Assuming that chatmessage is a direct child of your Firebase database root, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference deviceTokenRef = rootRef.child("chatmessage").child("devicetoken");
Query query = deviceTokenRef.orderByKey().limitToLast(1);
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) {}
};
query.addListenerForSingleValueEvent(eventListener);
Please how do I simply get the child count from a firebase Query. For example Let's say I use a database query with 10 children, how do I get that value because I tried using onChildChanged and getting the value from the snapshot, but it does not work well. This is because at first it will get the number, then it will query again because it has to continuously sync, but because there is not actual child change at the second query it return a value if zero which will replace the original correct value. :/
databaseReference.child("PrinterView").child(uni).child(phone).orderByChild("done").equalTo("No").addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
Toast.makeText(getActivity(),"Number:"+String.valueOf(dataSnapshot.getChildrenCount()),Toast.LENGTH_LONG).show();
}
#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) {
}
});
Both don't work
databaseReference.child("PrinterView").child(uni).child(phone).orderByChild("done").equalTo("No").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Toast.makeText(getActivity(),"Number:"+String.valueOf(dataSnapshot.getChildrenCount()),Toast.LENGTH_LONG).show();
Vendor_view_Home_fragment.printIndicator.setText(String.valueOf(dataSnapshot.getChildrenCount()));
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Assuming that PrinterView is a direct child of your Firebase root, please use the following code:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference yourRef = rootRef.child("PrinterView").child("Covenant University").child("588");
Query query = yourRef.orderByChild("done").equalTo("No");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
long count = dataSnapshot.getChildrenCount();
Log.d("TAG", String.valueOf(count));
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
query.addListenerForSingleValueEvent(eventListener);
The problem was this line:
databaseReference.child("PrinterView").child(uni).child(phone).orderByChild("done").equalTo("No").addValueEventListener(new ValueEventListener()
The variable "uni" and "phone" was also provided by a firebase Query E.G It was to get the University and phone number of the current user so I could not just put a static string there.
databaseReference.child("Users").child(mAuth.getCurrentUser().getUid()).child("Phone").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
phone = dataSnapshot.getValue().toString();
// Toast.makeText(getActivity(), phone, Toast.LENGTH_LONG).show();
setPendingList();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
So the issue was that, if the network connection was slow or if the queries were not done yet, those two variables would be empty thereby making the dataSnapshot empty. I will have to sort that out by only allowing that to be queried when the rest are sure to be done. :)