Set a data inside onDataChange method, and use it outside - java

I'm trying to get name data from Firebase using uid from another collection with an interface, set that data into a variable, and do some task. Then let's say I want to show a Toast if the task is done or not.
This is the method
public void createExcel(final String grade) {
Boolean taskSuccess;
DatabaseReference attendanceRef = FirebaseDatabase.getInstance().getReference().child("attendance").child(grade);
attendanceRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String thisUid;
for (DataSnapshot ds : snapshot.getChildren()) {
for (DataSnapshot ds2 : ds.getChildren()) {
thisUid = ds2.child("uid").getValue().toString();
readData(thisUid, new MyCallBack() {
#Override
public void onCallback(String value) {
thisName[0] = value;
//Do some task
if (//task success) {
taskSuccess == true;
}
}
});
}
}
System.out.println(taskSuccess); //taskSuccess value is always false
}
});
}
This is my interface
public interface MyCallBack {
void onCallback (String value);
}
This is my readData method
public void readData (String uid, final MyCallBack myCallBack) {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference().child("users").child(uid);
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
String value = snapshot.child("name").getValue().toString();
myCallBack.onCallback(value);
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
}
Is there any way I can set that taskSuccess value inside onDataChange method and use it outside onDataChange method? Because if I make a Toast inside readData it will make some Toasts because of the loop to get the data from database

Related

How to pass values from Callback into another method

private Flowable<List<String>> getInfo(){
List<String> myList = new ArrayList<>();
return BehaviorProcessor.create(emitter->{
FirebaseDatabase.getInstance().getReference("url")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (null != dataSnapshot) {
if (dataSnapshot.exists()) {
if (dataSnapshot.hasChildren()) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
final MyClass myClass= postSnapshot.getValue(MyClass.class);
String key = postSnapshot.getKey();
String firsnName = myClass.getFirstName();
myList.add(0, key);
myList.add(1, firsnName);
}
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}, BackpressureStrategy.LATEST);
}
private Flowable<GeoLocation> setMyLocation(){
Flowable.create(emitter -> {
geoFire.setLocation(key, new GeoLocation(myLastLocation.getLatitude(),
myLastLocation.getLongitude()), (k, e) ->{
});
});
}
I would like to pass the values in my list into my next method. Specifically, I need to pass my key into the setLocation method of GeoFire as the key as shown. I do understand onDataChange is called asynchronously and I am used to doing it in my Activity class using a callbackListener. How can I do the above in my ViewModel? I am open to change the whole implementation for a working solution.
I am using ViewModel, LiveDataReactiveStream in Android Java. I would hope to get help finding a solution using RxJava inside my ViewModel.
Thanks.
Don't use List, create your own object, say Info :
class Info {
private final String key;
private final String firstName;
public Info(String key, String firstName) {
this.key = key;
this.firstName = firstName;
}
...
}
Your getInfo then will be like :
private Flowable<Info> getInfo(){
return BehaviorProcessor.create(emitter->{
FirebaseDatabase.getInstance().getReference("url")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (null != dataSnapshot) {
if (dataSnapshot.exists()) {
if (dataSnapshot.hasChildren()) {
for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) {
final MyClass myClass= postSnapshot.getValue(MyClass.class);
String key = postSnapshot.getKey();
String firstName = myClass.getFirstName();
// use emitter here
emitter.onNext(new Info(key, firstName))
}
}
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
// use emitter here
emitter.onError(/*error*/)
}
});
}, BackpressureStrategy.LATEST);
}
and then change the signature of your setMyLocation to accept the key.
After that use concatMap operator to chain your requests :
getInfo()
.concatMap(info -> setMyLocation(info.key))
.subscribe();
Hope this helps

Retrieve the children of a parent node and perform a method on each value

I am building an app that needs to retrieve all child from a certain parent node and would like to perform a method call on each child retrieved.
Here is my current database:
Click Me
I would like to call all child of conradjr and on retrieval of a child for example "ranniecardino15", it should store it to a variable then perform a method call. How can I do this?
I have made a code like this but the method call "getCurrentLocation" is only performed on the last child retrieved.
public void getCurrentChildUser() {
DatabaseReference getuser = FirebaseDatabase.getInstance().getReference().child("Children");
getuser.child(user).orderByChild("CurrentLocation").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapshot : dataSnapshot.getChildren()) {
childuser = childSnapshot.getKey();
if (childuser != null){
getCurrentLocation();
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public void getCurrentLocation(){
DatabaseReference getuser = FirebaseDatabase.getInstance().getReference().child("Children");
getuser.child(user).child(childuser).child("CurrentLocation").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot != null){
currentloc = dataSnapshot.getValue(String.class);
System.out.println("Current Location: "+currentloc);
String split[] = currentloc.split(",");
lat1 = Double.parseDouble(split[0]);
lng1 = Double.parseDouble(split[1]);
System.out.println("Current Latitude: "+lat1+" and Current Longitude: "+lng1);
getSavedLocation();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
}
Please help me :) Thanks in advance :)
To solve this, you need to change the declaration of your getCurrentLocation() method from:
public void getCurrentLocation(){}
to
public void getCurrentLocation(String childuser){}
Now you need to call this method from inside the onDataChange() method like this:
getCurrentLocation(childuser);
See, I have passed the childuser as an argument to the getCurrentLocation() so it can be used in a proper way.

How to find if a child with particular value in its fields exists in Firebase?

I need to find if a child with particular value in one of its fields exists in Firebase.
Below is my database structure:
To check if a field exists,you can do this:
DatabaseReference ref=FirebaseDatabase.getInstance().getReference().child("bdxhATalcDRJCiZZcUFUJo3yrJF2");
ref.addValueEventListener(new ValueEventListener(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
//then it exists
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
This will check if
child("bdxhATalcDRJCiZZcUFUJo3yrJF2") exists in the database.
If you do this:
ref=FirebaseDatabase.getInstance().getReference().child("bdxhATalcDRJCiZZcUFUJo3yrJF2").orderByChild("chat_id").equalTo(chatid);
then it will check if that child also exists in the database
In your case you would set up query like this:
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference.orderByChild("chat_id").equalTo("CHAT_ID_To_COMPARE");
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot issue : dataSnapshot.getChildren()) {
// do something with the individual object
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
This would filter your data at server side and no need to filter data at app side.
postRef = FirebaseDatabase.getInstance().getReference().child("bdxhATalcDRJCiZZcUFUJo3yrJF2");
postRef.orderByChild("chat_id").equalTo(chat_id)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()){
//bus number exists in Database
} else {
//bus number doesn't exists.
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
etEmail is an EditText
I did it for userEmail's value... as I don't have any username other than the email.
for a structure like this...
"users": {
pushId: {
"userEmail": {
}
}
}
onCreate
etEmail.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(final CharSequence s, int start, int before, int count) {
//yourFunction();
DatabaseReference databaseUsers = FirebaseDatabase.getInstance().getReference().child("users");
databaseUsers.orderByChild("userEmail").equalTo(String.valueOf(s))
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
for (DataSnapshot issue : dataSnapshot.getChildren()) {
// do something with the individual object
//User email already exists
//do something
//return; stops the function from executing further
return;
}
} else {
// email doesn't exists.
// do something
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
#Override
public void afterTextChanged(Editable s) {
}
});
When you do the reference and you have your DataSnapshot, you can check it like this:
yourref.child("chat_id").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.exists()) {
//Do something
}
}
exists()
Returns true if the snapshot contains a non-null value.
Also returns true if this DataSnapshot contains any data. It is slightly more efficient than using DataSnapshot.getValue() !== null.

null variables out of onDataChange method

I'm currently developing an android app where I'm using firebase as database but when in got the variable in the onDataChange method and I assign them to a global variables I got null variables but when I call those variables in the onDataChange method they are not null.
public class PositionateMarkerTask extends AsyncTask {
public ArrayList<Location> arrayList= new ArrayList<>();
public void connect() {
//setting connexion parameter
final Firebase ref = new Firebase("https://test.firebaseio.com/test");
Query query = ref.orderByChild("longitude");
//get the data from the DB
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//checking if the user exist
if(dataSnapshot.exists()){
for (DataSnapshot userSnapshot : dataSnapshot.getChildren()) {
//get each user which has the target username
Location location =userSnapshot.getValue(Location.class);
arrayList.add(location);
//if the password is true , the data will be storaged in the sharedPreferences file and a Home activity will be launched
}
}
else{
System.out.println("not found");
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
System.out.println("problem ");
}
});
}
#Override
protected Object doInBackground(Object[] params) {
connect();
return null;
}
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
System.out.println("the firs long is"+arrayList.get(0).getLongitude());
}
}
Even I got the same problem. Just pass the arraylist values inside the onDataChange to another method.
myFirebaseRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Log.d("FIREBASE",String.valueOf(dataSnapshot.getChildrenCount()));
if (dataSnapshot.exists()){
for (DataSnapshot snapshot : dataSnapshot.getChildren()){
Log.d("FIREBASE ","FOR LOOP");
//Arraylist codes
iosTitleVal.add(snapshot.getKey().toString());
iosDescVal.add(snapshot.getValue().toString());
Log.d("FIREBASE","ArrayList KEY IS "+iosTitleVal);
//Passing arraylist values to another method
storageContainer(iosTermTitle,iosTermDesc);
}
}else {
Log.d("FIREBASE","NO DATAS");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Here's my storageContainer method. In this method I passed the arrraylist values to RecyclerView Adapter's constructor.
public void storageContainer(ArrayList<String> arrayListTitle, ArrayList<String> arrayListDesc){
iosTitleStorage = arrayListTitle;
iosDescStorage = arrayListDesc;
//No null exception here...
Log.d("FIREBASE"," My Data KEY "+iosTitleStorage +" and VAL "+iosDescStorage);
}
I hope it'll work

Retrieve Java Object from Firebase

I managed to get the object with this code:
public void retrieveUser(final String email){
firebaseUsersRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot messageSnapshot: dataSnapshot.getChildren()) {
if(messageSnapshot.getKey().equals(Email.encodeID(email))){
retrievedUser = messageSnapshot.getValue(User.class);
break;
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) { }
});
}
(Please note retrievedUser is a class attribute, thus a field.)
I am accessing that field from the code, but even I see it takes the value on the debugger, it is being null on the calling code.
Any hint? CanĀ“t I just return it in the method itself, so it would be?:
public User retrieveUser(final String email);
So, to sum up, how can I return the User object itself?
Thanks
Firebase loads and synchronizes data asynchronously. You can't be sure that your field retrievedUser will have a value when you access it because you don't know when Firebase will call the onDataChange event.
You have to do your work that depends on retrievedUser in the onDataChange event handler like this.
firebaseUsersRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot messageSnapshot: dataSnapshot.getChildren()) {
if(messageSnapshot.getKey().equals(Email.encodeID(email))){
retrievedUser = messageSnapshot.getValue(User.class);
doSomethingWithTheUser(retrievedUser);
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {}
});
You should be able to return the retrievedUser object using a callback or a return statment. Below is some code that shows how to return the value using a callback.
#Override
public void getEvents(final LoadEventsCallback callback) {
final List<Event> events = new ArrayList<Event>();
Firebase ref = new Firebase("https://austin-feeds-me.firebaseio.com/events");
ref.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
Event event = snapshot.getValue(Event.class);
events.add(event);
}
callback.onEventsLoaded(events);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}

Categories