Android Firebase addListenerForSingleValueEvent pass value - android

I have button in a fragment, which upon click should check if data exists in firebase db or not. Below is the function in a separate class file that will be called on button click in an async task.
How can I return boolean value true/false from addListenerForSingleValueEvent back to the fragment async task?
void checkDataExists(final String mobile){
DatabaseReference fireDBRef = FirebaseDatabase.getInstance().getReference(context.getString(R.string.app_name);
fireDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String mob =
String.valueOf(dataSnapshot.child(context.getString(R.string.tracked_mobile))
.getValue());
//compare the strings mobile
boolean match = mobile.equals(mob);
// return match value to fragment to update the view.
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.w(TAG + "/checkDataExists","Data read from DB failed: " + databaseError.getMessage());
}
});
}

I also have case like this, and I create my own callback like:
public interface IMyCallback {
void onSuccess(boolean isExist);
void onFailure(String error);
}
now when I call function checkDataExists it looks like:
checkDataExists(mobile, new ISingUpCallback() {
#Override
public void onSuccess(boolean isExist) {
}
#Override
public void onFailure(String error) {
}
});
And in your check you need to make changes like:
void checkDataExists(final String mobile, final IMyCallback callback){
DatabaseReference fireDBRef = FirebaseDatabase.getInstance().getReference(context.getString(R.string.app_name);
fireDBRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String mob =
String.valueOf(dataSnapshot.child(context.getString(R.string.tracked_mobile))
.getValue());
//compare the strings mobile
boolean match = mobile.equals(mob);
// return match value to fragment to update the view.
callback.onSuccess(match);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
callback.onFailure(databaseError.getMessage());
}
});
}

Related

Intent is not passing on single click with loading firebase data

In my code, when user click on a button, it calls a function.In that function data has to loaded from firebase. On successful of loading, Intent will pass to another activity with some putExtra data.
SharedPreferences pref = getApplicationContext().getSharedPreferences("My_pref", MODE_PRIVATE);
choice = pref.getInt("language", 0);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(choice==1)
{
firebase("param1","param2","English");
}
if(choice==2)
{
firebase("param1","param2","hindi");
}
}
});
public void firebase(String files,String titles,String language)
{
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(language);
DatabaseReference myRef1=myRef.child(titles);
DatabaseReference myRef2=myRef.child(files);
myRef1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_title = dataSnapshot.getValue(String.class);
t=1;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
myRef2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_file= dataSnapshot.getValue(String.class);
f=1;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
if(t==1&&f==1) {
Intent i = new Intent(getApplicationContext(), caf.class);
i.putExtra("titles", language_title);
i.putExtra("files", language_file);
startActivity(i);
}
}
But in that case intent is only passing when I click twice.In single click Intent is not passing. where is the problem?
Any code that needs data from the database, needs to be inside the onDataChange method or be called from there.
See my answer here for a longer explanation: getContactsFromFirebase() method return an empty list.
Since you have two onDataChange methods, you'll need to make sure both have been called before starting the new activity. Using some simple flag variables to track state, you can do that with:
Declare two member fields:
boolean isTloaded, isFLoaded;
And then
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference(language);
DatabaseReference myRef1=myRef.child(titles);
DatabaseReference myRef2=myRef.child(files);
isTloaded = false;
isFloaded = false;
myRef1.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_title = dataSnapshot.getValue(String.class);
t=1;
isTloaded = true;
if (isTloaded && isFloaded) {
startCafActivity();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
myRef2.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
language_file= dataSnapshot.getValue(String.class);
f=1;
isFloaded = true;
if (isTloaded && isFloaded) {
startCafActivity();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
And then finally:
private void startCafActivity() {
if(t==1&&f==1) {
Intent i = new Intent(getApplicationContext(), caf.class);
i.putExtra("titles", language_title);
i.putExtra("files", language_file);
startActivity(i);
}
}
It might be even simpler, but I had a hard time figuring out your application logic due to the variable names. Consider using more descriptive named than t, f and caf to make it easier for others (and your future self) to understand what each part of the code does in isolation.

Trying to replace a data from specific child (Firebase Database)

I want to replace kids/[id]/kidLimit/[new data]
So what I do is I make a spinner with kidName data, then with the selected item from that spinner I want to replace the data of kidLimit (with the same parent as the selected item from the spinner).
The first thing I do is to find the unique key of the selected item, then go to the kidLimit with that unique key to then use the setValue() method.
public class setLimit extends AppCompatActivity {
private DatabaseReference db;
private EditText etLimit;
private Button btnSetLimit;
private Spinner kidSpinner;
private String kidKey;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_set_limit);
etLimit = findViewById(R.id.et_limit);
btnSetLimit = findViewById(R.id.btn_confirm);
kidSpinner = findViewById(R.id.spinner_kid);
//PUTTING STRING LIST FROM FIREBASE TO SPINNER DROPDOWN STARTS HERE
db = FirebaseDatabase.getInstance().getReference().child("kids");
db.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
final List<String> kid = new ArrayList<>();
for (DataSnapshot dataSnapshot1: dataSnapshot.getChildren()) {
String kidName = dataSnapshot1.child("kidName").getValue(String.class);
kid.add(kidName);
}
ArrayAdapter<String> kidNameAdapter = new ArrayAdapter<>(setLimit.this, android.R.layout.simple_spinner_item, kid);
kidNameAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
kidSpinner.setAdapter(kidNameAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
//ENDS HERE
//ONCLICKLISTENER
btnSetLimit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
findId();
String newLimit = etLimit.getText().toString().trim();
db.child(kidKey).child("kidLimit").setValue(newLimit);
}
});
}
//FINDING KEY FROM THE SELECTED SPINNER ITEM
public void findId(){
String kidName = kidSpinner.getSelectedItem().toString();
db = FirebaseDatabase.getInstance().getReference().child("kids");
db.orderByChild("kidName").equalTo(kidName).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren()){
kidKey = dataSnapshot1.getKey();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
}
So basically what I did was using the snapshot inside the onclick method to find the key. But when I check them on the log, it showed that the kidkey variable is null the first time I press the button, but after that it's not null anymore.
What can I to do so when I press the button, I can go to a specific child to replace it's data without getting any null pointer exception?
Here's the database that I use
When you do this
btnSetLimit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
findId();
String newLimit = etLimit.getText().toString().trim();
db.child(kidKey).child("kidLimit").setValue(newLimit);
}
});
findId(); is executed but you don't know when it will finish, so after that method is executed and waiting for data, this line
db.child(kidKey).child("kidLimit").setValue(newLimit);
will have kidKey with a null value because it has not been pulled yet from the database, so instead, you should move your code to the onDataChange() or make a new callback when all the asynchronous process finishes
btnSetLimit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
findId();
}
});
public void findId(){
String kidName = kidSpinner.getSelectedItem().toString();
String newLimit = etLimit.getText().toString().trim();
db = FirebaseDatabase.getInstance().getReference().child("kids");
db.orderByChild("kidName").equalTo(kidName).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for(DataSnapshot dataSnapshot1:dataSnapshot.getChildren()){
kidKey = dataSnapshot1.getKey();
}
db.child(kidKey).child("kidLimit").setValue(newLimit);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});

Android - Insert in DB if value is greater than the value in DB

I am using Android studio and Firebase to develop an application. I want to store a score in the database if the value is greater than the score in database. How do I check for that?
userData.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
userData.child("score").setValue(count);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
You use the dataSnapshot to get the value from the reference.
Then, use that reference to update the data as you have, but add the conditional.
userData.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final String scoreKey = "score";
Long oldScore = dataSnapshot.child(scoreKey).getValue(Long.class);
if (oldScore == null || count > oldScore) {
userData.child(scoreKey).setValue(count);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

Android - Firebase many to many relationship child listener

My firebase database has the following structure:
tasks
- task_key_1:{
- name: "task 1"
- ...
- users{
user_key_1 : true
}
}
users
- user_key_1{
tasks{
task_key_1 : true
}
}
Now, I would like to display a list of an user's tasks in an activity. I found this method and it's working:
myTasks.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
taskReference.child(dataSnapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
...
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, databaseError.toString());
}
});
}
public void onChildRemoved(DataSnapshot dataSnapshot) {
taskReference.child(dataSnapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot taskSnapshot) {
......
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, databaseError.toString());
}
});
}
public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
taskReference.child(dataSnapshot.getKey()).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot taskSnapshot) {
Log.w(TAG, "on child changed")
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, databaseError.toString());
}
});
});
}
public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
Log.w(TAG, "onChildMoved");
}
#Override
public void onCancelled(DatabaseError error) {}
});
where:
mytasks is a reference to /users/userId/tasks
and taskReference is /tasks/taskKeyOwnedByUser.
My problem is: how can I detect changes to a task, i.e. when the user changes its title, and then update the list UI? It seems that the "Log.w" inside OnChildChanged never gets called.
Thank you in advance
This is my approach, change the value type of users/<usersId>/tasks/<taskId> to a timestamp value. Then when you are about to update the task, make sure to also update the timestamp so the onChildChanged will be triggered.
This is your database structure would be
users {
- user_key_1 {
tasks {
task_key_1 : ServerValue.TIMESTAMP (Long value)
}
}
}
And this is an example method when you are about to update the task's name
Map<String, Object> values = new HashMap<>();
values.put("tasks/" + task_key + "/name", new_task_name);
values.put("users/" + user_key + "/tasks/" + task_key, ServerValue.TIMESTAMP);
ref.updateChildren(values);
With this approach, the onChildChanged will be fired.
Hope this helps :)

Firebase load data onCreate()

I want to load a dataset oncreate().
Since the data will not change, I use the one time loader:
protected void onCreate(Bundle savedInstanceState) {
....
mDatabase.child("users").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentUser = dataSnapshot.getValue(Users.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
String name = currentUser.getName();
....
}
But the data is not available, the getName function is called on a null object.
How can I get the data onCreate()?
The problem is that Firebase uses an asynchronous listeners to a database reference, and you set name variable with null currentUser.
Try to declare the name var as final and before the listener, and set his value inside the firebase callback.
Update:
an example how to implement and use an "override" function:
final String name;
mDatabase.child("users").child(userId).addListenerForSingleValueEvent(
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentUser = dataSnapshot.getValue(Users.class);
name = currentUser.getName();
fooFunc(name); // calling a function with data
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.w(TAG, "error:", databaseError.toException());
}
});
#Override
public void fooFunc(String name) {
// name != null, when called from the the callback
// dont forget to check it
}

Categories

Resources