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.
Related
I'm trying to get the count of "msanID" but it always returns the number as '0'. I'm using a separate class (ManageNodeName) to record the data and retrieving them back at list view.
I suspect the error is in the method of getting the String MSAN_NAMESP.
Any lead may help. Thanks!
List View
databaseNodes = FirebaseDatabase.getInstance().getReference("MSAN List");
#Override
protected void onStart() {
super.onStart();
databaseNodes.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
manageNodeList.clear();
for(DataSnapshot nodesnapshot: dataSnapshot.getChildren()){
progressDialog.dismiss();
ManageNodeName manageNodeName = nodesnapshot.getValue(ManageNodeName.class);
manageNodeList.add(manageNodeName);
MSAN_NAMESP = manageNodeName.getNodeID();
}
ManageNodeList adapter = new ManageNodeList(ManageSelectMSAN.this, manageNodeList);
manageListViewMSANs.setAdapter(adapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
query = FirebaseDatabase.getInstance().getReference("MSAN MTCE")
.orderByChild("msanID")
.equalTo(MSAN_NAMESP);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
int size = (int) dataSnapshot.getChildrenCount();
ManageNodeName.setCount(size);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
Data is loaded from Firebase (and most cloud APIs) asynchronously. While the data is being loaded, your main code continues to run, so that the user can continue to use the app. Then when the data is available, your onDataChange is called with that data.
This means that all code that needs the data, needs to either be inside onDataChange or be called from there.
So:
databaseNodes.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
manageNodeList.clear();
for(DataSnapshot nodesnapshot: dataSnapshot.getChildren()){
progressDialog.dismiss();
ManageNodeName manageNodeName = nodesnapshot.getValue(ManageNodeName.class);
manageNodeList.add(manageNodeName);
MSAN_NAMESP = manageNodeName.getNodeID();
query = FirebaseDatabase.getInstance().getReference("MSAN MTCE")
.orderByChild("msanID")
.equalTo(MSAN_NAMESP);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
int size = (int) dataSnapshot.getChildrenCount();
ManageNodeName.setCount(size);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
}
ManageNodeList adapter = new ManageNodeList(ManageSelectMSAN.this, manageNodeList);
manageListViewMSANs.setAdapter(adapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
throw databaseError.toException(); // never ignore errors
}
});
Also see: getContactsFromFirebase() method return an empty list for a longer example, and alternative approaches.
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) {
}
});
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());
}
});
}
I have the following code for deleting some data form Firebase:
public void removeGameFromWaitQueue() {
final DatabaseReference data = FirebaseDatabase.getInstance().getReference().child("games").child(mCurrentGameKey);
data.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
mCurrentGame = snapshot.getValue(DatabaseModels.Game.class);
if (mCurrentGame.getState() == DatabaseModels.Game.State.OPEN.ordinal()){
data.removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
How can i check if "data" exists, then execute all the else code? Meaning that if i create the DatabaseReference, code works. But of course if i dont create the reference, then i have null exceprion. Should i have 2 listeners for this? And if yes, how is the best way to do it?
For future reference i solved it like this:
public void removeGameFromWaitQueue() {
final DatabaseReference data = FirebaseDatabase.getInstance().getReference().child("games").child(mCurrentGameKey);
data.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.exists()) {
mCurrentGame = snapshot.getValue(DatabaseModels.Game.class);
if (mCurrentGame.getState() == DatabaseModels.Game.State.OPEN.ordinal()){
data.removeValue();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
public void removeAfterCheck(){
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
rootRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.hasChild("games")) {
removeGameFromWaitQueue();
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Probably there is a better way, but this works for me in all cases as i want.
The code looks like this:
final DatabaseReference TuidRef = usersRef.child(td);
final DatabaseReference msgRef = TuidRef.child("rec_msg");
final DatabaseReference FuidRef = TuidRef.child("fromUID");
final DatabaseReference secretRef = TuidRef.child("rec_secret");
msgRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot1) {
ms = dataSnapshot1.getValue(String.class);
flag++;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
FuidRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot2) {
fUid = dataSnapshot2.getValue(String.class);
flag++;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
secretRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot3) {
st = dataSnapshot3.getValue(String.class);
flag++;
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
String flagS = "" + flag;
Log.i("flag",flagS);
if(flag > 2)
displayMessage();
I used the flag to know how many times the ValueEventListeners are triggered, but the flag is found to be 0, in the log.
td is the uid of the person who receives the message. In the picture below it is same as the parent key of other sibling Childs.
The database looks something like this, when there are no messages:
The database looks like this, when messages are received:
Edit: How can I execute the function displayMessage(), when all three of the listeners have been triggered at once?
The value is added simultaneously to all three of them in the firebase database, though.
You'll never know how many times a ValueEventListeners is triggered in the way do, because the value of flag will always remain 0. This is happening due the asynchronous behaviour of onDataChange() method which is called even before you are trying to get the data from the database. What's really happening is that you are trying to get the data from the database and immediately try to log it while the flag variable has the initial value of 0. Then, when getting the data from the database completes, it changes flag's value, but it's never read again.
A quick solve for this problem would be add three different flags inside each callback and try to log then separately only inside the onDataChange() method like this:
msgRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot1) {
ms = dataSnapshot1.getValue(String.class);
flag1++;
Log.i("flag1", String.valueOf(flag1));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
FuidRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot2) {
fUid = dataSnapshot2.getValue(String.class);
flag2++;
Log.i("flag2", String.valueOf(flag2));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
secretRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot3) {
st = dataSnapshot3.getValue(String.class);
flag3++;
Log.i("flag3", String.valueOf(flag3));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
If you need in your code to get the value of that flag outside the onDataChange() method, I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
Edit:
msgRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot1) {
ms = dataSnapshot1.getValue(String.class);
flag++;
FuidRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot2) {
fUid = dataSnapshot2.getValue(String.class);
flag++;
secretRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot3) {
st = dataSnapshot3.getValue(String.class);
flag++;
Log.i("flag", String.valueOf(flag));
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {}
});