How to stop Firebase database infinite looping - android

I am creating an app and part of it's features in user interaction. I want to store user comments on a post and I do not want to limit the amount of comments any one user can make. I do this by assigning a random number as the .setValue of the database entry.
With this implementation, whenever the comment is sent to the database it is stuck in an infinite loop where it will continually update with the same string entered in the text box but it will constantly generate new posts.
Full code;
sendComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
commitPost();
finish();
}
});
}
private void commitPost() {
commentProgress.setMessage("Posting");
commentProgress.show();
final String commentInput = commentText.getText().toString().trim();
if(TextUtils.isEmpty(commentInput)){
Snackbar.make(commentLayout,"You haven't finished your post yet",Snackbar.LENGTH_SHORT);
}else{
commentDB.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String uniqueId = UUID.randomUUID().toString();
commentDB.child(postID).child("Comments").child(uniqueId).setValue(commentInput);
commentProgress.dismiss();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
The problem lies in the commentDB.addValueEventListener

The problem is that you set a listener for data changes and you also change data inside it (so it is called).
You don't need the listener, just add:
else {
String uniqueId = UUID.randomUUID().toString();
commentDB.child(postID)
.child("Comments")
.child(uniqueId)
.setValue(commentInput);
commentProgress.dismiss();
}

Related

Saving Total Score in Firebase under Login Node

I am a beginner and I am making a simple android game in which a user sign in into the app and play the game. After the game finishes I want to add the current score of the user with previous score and then store this total score in Firebase and then again retrieve the score next time when the user plays game.
I am not getting on how should I save the Total score for every different user who sign in.
this is my firebase json tree
https://drive.google.com/file/d/1T7-x3TP1TaA8_ntwfoRNdb2oMGV_swl6/view?usp=sharing
private void updateScore() {
TotalScore = new Firebase("https://bscitquiz.firebaseio.com/Users/" + username +"/highScore");
TotalScore.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
Integer totalscore = dataSnapshot.getValue(Integer.class);
totalscore = totalscore + mScore;
dataSnapshot.getRef().setValue(totalscore);
HighScore.setText("" +totalscore);
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
You can save each user's score under its name at the database and just update it each time.
the android java code is written like this -
public static void updateScore(Integer score, String userId) {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference();
ref.child("Users").child(userId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override public void onDataChange(DataSnapshot dataSnapshot) {
Integer previousScore = dataSnapshot.getValue(Integer.class);
if (previousScore != null){
previousScore = previousScore + score;
dataSnapshot.getRef().setValue(previousScore);
}
}
#Override public void onCancelled(DatabaseError databaseError) {
Log.e("frgrgr", "onCancelled", databaseError.toException());
}
});
}
This way you retrieve the old score, update it, and send back to the database.
Now at this way you have only the score value under each user.
You can use the user's name for the child ref of "Users" if its unique or just generate an Id for each user.
Edit after understanding your need for a callback -
callback in android?
For adding a callback there are a few steps,
open a new java class with the following code -
public interface FireBaseCallbacks {
interface GetScoreCallback {
public void onGetScoreComplete(Integer score)
}
}
add the callback parameter to your updateScore method -
private void updateScore(FireBaseCallbacks.GetDevicesCallback callback) {
...
}
call the callback parameter after you get the value -
Integer totalscore = dataSnapshot.getValue(Integer.class);
callback.onGetScoreComplete(totalscore)
call the method somewhere in your activity (at the point that you want to update the score) with this as a parameter to tell the callback where to come back to (regarding the activity) -
updateScore(this)
now for the last step, implement the FireBaseCallbacks.GetScoreCallback to your activity and then you'll be forced to implement its onGetScoreComplete method. implement it and inside it you get the score as a parameter to do what ever you wish :)
Hope this will help, let me know otherwise.
You can have a path for score under User (for Eg: User/userId/score) where you can update the score each time after completing the game. Below is the javascript code.
firebase.database().ref('User').child(userId).child('score').set(currentScore).then(result => {
const user = snap.val()
const userKey = snap.key
}).catch (function(err) {
console.error(err)
})
Here currentScore is the value you want to update.
You can go through this link if you want it in android: https://firebase.google.com/docs/database/android/read-and-write

Firebase updating password (current pass and new pass)

I've been struggling about the change password functionality of my app. I am confused on how I can compare the current pass and store the new password thru firebase database.
Please don't be harsh on me on comments, educate me please. I did research first before asking and tried several times. Most of the tutorials I found are about updating data while clicking an item in listview. The reason why I use db to store users is because I am going to create 2 keys that identifies if user is student or professor.I just want to ask help how am I able to implement change of password.
ChangePassAccount.class
public class ChangePassAccount extends AppCompatActivity {
Button btnSave;
EditText cpass, npass;
String pass, newpass;
DatabaseReference dbPDF;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getSupportActionBar().hide();
setContentView(R.layout.activity_change_pass_account);
dbPDF = FirebaseDatabase.getInstance().getReference("users").child("password");
cpass = (EditText)findViewById(R.id.currentpass);
npass = (EditText)findViewById(R.id.newpass);
btnSave = (Button) findViewById(R.id.btnsave);
btnSave.setBackgroundResource(R.drawable.button);
btnSave.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
pass = cpass.getText().toString();
newpass = npass.getText().toString();
}
});
}
}
i'll suggest you to using firebase auth to manage User Login or Changes password etc.. So maybe you only has to store user Data by UserInfo
this is a sample from Firebase-Manage User to change the user password
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String newPassword = "SOME-SECURE-PASSWORD";
user.updatePassword(newPassword)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "User password updated.");
}
}
});
This is not a very technical answer but I ran into this problem in iOS recently.
There is a FirebaseAuth method named 'reauthenticate' that can be found here. This takes a credential that you create by asking for the user's password and getting the email from currentUser. If this returns successful you will know that password is correct. After reauthenticated you can call updatePassword with the new password.
As a suggestion you should not need to store the password separately in the database since Firebase will handle all this for you.
Hope this helps out, good luck!
You'd use a Firebase Database transaction for that. This gives you the current value, which you then compare against the old password. If they match, you return the new password.
Something like:
passwordRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
String password = mutableData.getValue(String.class);
if (password.equals(oldPassword) {
mutableData.setValue(newPassword);
return Transaction.success(mutableData);
}
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
Log.d(TAG, "passwordTransaction:onComplete:" + databaseError);
}
});
First you need to read the current password using single value event listener
pass = cpass.getText().toString();
newpass = npass.getText().toString();
dbPDF.addValueEventListener(addListenerForSingleValueEvent(){
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
String password = dataSnapshot.getValue(String.class);
if(password.equals(pass)){
dbPDF.setValue(newpass);
}else{
// show wrong pass dialog
}
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
Also make sure dbref is correct
String username = "helpmepls";
dbPDF = FirebaseDatabase.getInstance().getReference("users").child(username).child("password");

Working with EventListener in Firebase for Android

Consider, I store the following Key-Value pair in Firebase Database.
Key: "CarBrand" and Value: "Audi"
I read the Value for this Key from Firebase Database and display the Value in TextView. For this, I use an EventListener.
The problem I face with an EventListsner is since it runs on a separate thread, the TextView returns a NullPointerException even before the Value is fetched from Firebase.
To overcome the issue I have been using this dirty trick (using a Handler with 500 to 1000 ms delay). Can someone guide me the right way to fetch and display the Value in TextView or any other Views
PS: Apologies for this codeless Question
Thanks
EDIT: More the Code, better the question. The sample code is as follows
String CardBrand;
private ValueEventListener contentListener() {
contentListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Pull Values for all available Keys
CarBrand = dataSnapshot.child("CarBrand").getValue(String.class);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
return contentListener;
}
dbContent.addListenerForSingleValueEvent(contentListener());
mTextView.setText(CarBrand);
The above code results in error. To avoid this, I replaced the last line of code with
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
mTextView.setText(CarBrand);
}
}, 1000);
So this is what I have been doing. What would be the right way to display the Text in TextView in my Case?
Your approach is wrong as the listener is updating the String CarBrand But since the code is not in sync and casing the Error. You need to update the code
private ValueEventListener contentListener() {
contentListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Pull Values for all available Keys
String CarBrand =
dataSnapshot.child("CarBrand").getValue(String.class);
updateBrand(CarBrand);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
return contentListener;
}
dbContent.addListenerForSingleValueEvent(contentListener());
you need to create a Function to update the value
/* updates the Car brand Text
TODO:: You can do anystuff you want to do after anything gets
updated
*/
public void updateBrand(String brand){
mTextView.setText(CarBrand);
}
NOTE : Put Listener on the Data you are actually looking for changes.
If you want to work with the result of some callback (like ValueEventListener) you should execute your code inside the overriding methods.
String cardBrand;
private ValueEventListener contentListener() {
contentListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//Pull Values for all available Keys
carBrand = dataSnapshot.child("CarBrand").getValue(String.class);
mTextView.setText(carBrand);
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
return contentListener;
}
dbContent.addListenerForSingleValueEvent(contentListener());
Let me show you small example:
This class responsible for retreiving the link for you, and it doesn't know what will you do with it, so it takes Callback and pass the result to your callback, where you can do anything you want.
public class SomeDataSource {
void getData(Callback callback) {
String url = Backend.GetUrl();
callback.onDownloadUrlReceived(url);
}
}
this is description of callback. Which you can implement to handle data from SomeDataSource
public interface Callback {
void onDownloadUrlReceived(String url)
}
usage
void onCreate(Bundle savedInstanceState) {
SomeDataSource downloader = new SomeDataSource();
//here callback is implemented, and you can handle data from SomeDataSource like you want.
downloader.getData(new Callback() {
#Override
public void onDownloadUrlReceived(String url) {
do wat you want with url
}
});
}

Read Firebase Data Once With ValueEventListener

Within my app I often have the need to read data once. I originally started by using the addListenerForSingleValueEvent() method for this, however I ran into problems using this method as it does not work as I wanted when offline capabilities are enabled (see here the issue: Firebase Offline Capabilities and addListenerForSingleValueEvent)
In the question above it is mentioned that a workaround is to use the addValueEventListener() method, however I do not fully understand how to do this (particularly how to remove the ValueEventListener as soon I am finished grabbing the data I need).
Take this method which I created in a standalone class to query the Users node on Firebase where I store the users FCM Token. It seems to have an issue of not returning the latest token from the server everytime.
public class SendFCMMessage {
String userToken;
String currentUser;
String userName;
ValueEventListener userListener;
public void sendMessage(final String contactNumber) {
final DatabaseReference ref = FirebaseDatabase.getInstance().getReferenceFromUrl(link).child("Users").child(contactNumber);
userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
User user = dataSnapshot.getValue(User.class);
userToken = user.getToken();
// Send FCM Message after getting user token and then remove event listener
ref.removeEventListener(userListener);
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d("TAG", "Something terrible went wrong: " + databaseError);
}
};
ref.addValueEventListener(userListener);
}
}
If I remove the line
ref.removeEventListener(userListener);
Then this code works fine, however I would like to know how I could remove the ValueEventListener as soon as I receive the data I need?
Thanks,
R
ValueEventListener vel; //Declared Global
Listen your DatabaseReference like this;
vel = yourDatabaseReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChanged(DataSnapshot dataSnapShot) {
//Do your stuff here. I suggest you create another method for this if you don't want a problem with inner class.
//For example, workDataSnapshot(DataSnapshot dataSnapShot) <-- Work here
yourDatabaseReference.removeEventListener(vel);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Hope it helps you.

Android for loop not looping ? Firebase

I have an issue with the following code : I don't understand why my for-loop doesn't loop before if (gameExists[0] == false){... is called.
Button playButton = (Button) findViewById(R.id.play_button);
playButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
user = mFirebaseAuth.getCurrentUser();
final String gameCateg = String.valueOf(categorySelected[0]);
DatabaseReference allExistingGamesToMatchKeys = mFirebaseDatabase.getReference().child("gamestomatchkeys");
allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final boolean[] gameExists = {false};
Log.d("gameExists before loop ", String.valueOf(gameExists[0]));
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
final String currentKey = postSnapshot.getKey();
//check player 2 is not the same as player 1 so that we don't match the same player
if(gameCateg.equals(postSnapshot.getValue().toString())){
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String namePlayer1 = dataSnapshot.getValue(String.class);
if(!(user.getUid().toString().equals(namePlayer1))){
mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
gameExists[0] = true;
Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));
Intent intent = new Intent(getApplicationContext(), Game.class);
intent.putExtra("gameIdKey", currentKey);
startActivity(intent);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
break;
}
}
if (gameExists[0] == false){
Log.d("gameExists in if", String.valueOf(gameExists[0]));
This is what I get in my logs, in this order :
gameExists before loop: false
gameExists in if: false
gameExists in for loop: true
I don't understand why I get
gameExists in if: false
before
gameExists in for loop: true
I want my loop to be called and entirely looped before if (gameExists[0] == false){..., what should I modify ?
Thank you !
To make it simple, Firebase Database request are outside of the code flow. Take a look at this:
// here is first point
allExistingGamesToMatchKeys.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// here is second point
}
...
}
// here is third point
Above example will be executed like this
--> first point --> third point
Then where is the second point? Second point will be executed whenever Firebase get data from online database, so it is outside the flow (but most of the time, it will be executed after the third point.
So in conclusion, if you need some code to be executed after Firebase requests is done, place it inside onDataChange()
You might look at this for reference
It's because "gameExists in for loop" is not actually in the for loop. It's in a callback that is created in the for loop.
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String namePlayer1 = dataSnapshot.getValue(String.class);
if(!(user.getUid().toString().equals(namePlayer1))){
mFirebaseDatabase.getReference().child("gamestomatchkeys").child(currentKey).removeValue();
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player2").setValue(user.getUid());
gameExists[0] = true;
Log.d("gameExists in for loop ", String.valueOf(gameExists[0]));
Intent intent = new Intent(getApplicationContext(), Game.class);
intent.putExtra("gameIdKey", currentKey);
startActivity(intent);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Here you are creating a new instance of a ValueEventListener and you are overriding methods within it. Your are not executing the code within those methods at the point of instantiation. That code get called whenever your ValueEventListener decides to call the onDataChange() method.
I'm not entirely sure what this object is, or how it works:
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1")
But if I were you, I would start by seeing if I could get a DataSnapshot from it, and using that snapshot on whatever code you want to execute outside of the Listener.
If I had to guess, there might be a method like:
mFirebaseDatabase.getReference().child("games").child(currentKey).child("player1").getDataSnapshot();
that you can call. Then copy all the code from inside onDataChange() to outside of the listener.
(I have no idea if that method exits, but I would assume there is some way of getting the current DataSnapshot)

Categories

Resources