There can be mutiple people on the app click a button that will Increment value in firebase and get that value back, but if each person clicks the button at the same time right now, they will get the same value, how do I Increment with each person getting a differnt value?
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("queue");
reference.child(queue_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentNum = (long) dataSnapshot.child("currentNumber").getValue();
++currentNum;
dataSnapshot.child("currentNumber").getRef().setValue(++currentNum);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
To prevent conflicts from multiple users updating the same node at (almost) the same time, you need to use a transaction. In your case that'd look something like:
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("queue");
DatabaseReference numberRef = reference.child(queue_id).child("currentNumber");
numberRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Long value = mutableData.getValue(Long.class);
if (value == null) {
mutableData.setValue(1);
}
else {
mutableData.setValue(value+1);
}
return Transaction.success(mutableData);
}
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "runTransaction:onComplete:" + databaseError);
}
As per your code, you're incrementing the counter by 1 but you are not posting the same in the database. Use HashMap to update the data in your database when you modify it.
You can try the below code:
button.setOnClickListerner(new View.onClickListener() {
DatabaseReference reference = FirebaseDatabase.getInstance().getReference("queue");
reference.child(queue_id).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
currentNum = (long) dataSnapshot.child("currentNumber").getValue();
long newValue = currentNum + 1;
HasMap<String, Object> hashMap = new HashMap<>();
hashMap.put ("currentNumber", newValue);
reference.child(queue_id).updateChildren(hashMap);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
};
Hope this works!
You can use method transactions. Refer to this link: https://firebase.google.com/docs/database/android/read-and-write#save_data_as_transactions"
private void onStarClicked(DatabaseReference postRef) {
postRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Post p = mutableData.getValue(Post.class);
if (p == null) {
return Transaction.success(mutableData);
}
if (p.stars.containsKey(getUid())) {
// Unstar the post and remove self from stars
p.starCount = p.starCount - 1;
p.stars.remove(getUid());
} else {
// Star the post and add self to stars
p.starCount = p.starCount + 1;
p.stars.put(getUid(), true);
}
// Set value and report transaction success
mutableData.setValue(p);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
}
Related
Hi I want to read the display data from RecyclerView and make comparison.
This my layout for the activity:
What I want to do is to read all data from RecyclerView and compare with Daily Calorie Suggestion.
After reading all data, I need to make comparisons on how many times the user have taken above, less or sufficient total calories as shown in the "Analysis of Total Calories Consumed of Last 7 Days"
The code:
#Override
protected void onStart() {
Query query = ref.orderByChild("timeStamp").limitToLast(7).endAt(Date);
super.onStart();
if (query != null) {
query .addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
userHighlights = new ArrayList<>();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
userHighlights.add(ds.getValue(HighightsModel.class));
requiredCalorieRef = FirebaseDatabase.getInstance().getReference("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid());
requiredCalorieRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String userCalorieSuggestion = String.valueOf((dataSnapshot.child("daily calorie").getValue()));
int daily_calorie = Integer.parseInt(userCalorieSuggestion);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
HighlightsAdapter highlightsAdapter = new HighlightsAdapter(userHighlights);
highlightsRV.setAdapter(highlightsAdapter);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Toast.makeText(UserNewHighlights.this, databaseError.getMessage(),
Toast.LENGTH_SHORT).show();
}
});
}
}
This is my firebase:
Do I have to write a new code to solve this problem or else? Any help will be much appreciated. Thanks
As #MasoudDarzi mentioned, it's not related to the RecyclerView.
You can try something like this:
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
userHighlights = new ArrayList<>();
for (DataSnapshot ds : dataSnapshot.getChildren()) {
userHighlights.add(ds.getValue(HighightsModel.class));
requiredCalorieRef = FirebaseDatabase.getInstance().getReference("Users").child(FirebaseAuth.getInstance().getCurrentUser().getUid());
requiredCalorieRef.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String userCalorieSuggestion = String.valueOf((dataSnapshot.child("daily calorie").getValue()));
int daily_calorie = Integer.parseInt(userCalorieSuggestion);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
HighlightsAdapter highlightsAdapter = new HighlightsAdapter(userHighlights);
highlightsRV.setAdapter(highlightsAdapter);
// do calculation here with userHighlights
int countExceeded = 0, countBelow = 0, countSufficient = 0;
for (HighightsModel h : userHighlights) {
if (h.totalCalorie > daily_calorie) {
countExceeded++;
} else if (h.totalCalorie < daily_calorie) {
countBelow++;
} else {
countSufficient++;
}
}
// update your TextView with the count numbers
// todo
}
}
so The brute force solution is to have a for in your data after getting the 7 days average.
for (your 7 days data){
// check if your data is lower or higher than the average
// and store number of higher or lower
}
looking for a better solution?
I have solved my problem by adding another child at History node. Whereby, previously I have only these for History :
but I add new child which is called STATUS, the status is updated from previous activity as seen the pic below:
so what I did for the code is :
private void upDateAnalysisAbove() {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("History");
DatabaseReference mRef = ref.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
Query mQuery = mRef.orderByChild("status").equalTo("ABOVE").limitToLast(7);
mQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = String.valueOf(dataSnapshot.getChildrenCount());
int values = Integer.parseInt(value);
txt_above_output.setText(values + " times(s)");
upDateAnalysisLess(values);
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
//LESS
private void upDateAnalysisLess(int values) {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("History");
DatabaseReference mRef = ref.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
Query mQuery = mRef.orderByChild("status").equalTo("LESS").limitToFirst(7);
mQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String valueLess = String.valueOf(dataSnapshot.getChildrenCount());
int values_Less = Integer.parseInt(valueLess);
if (values_Less == 0){
txt_less_output.setText(values_Less + " times(s)");
}
if (values > values_Less){
int finalCount = values - values_Less ;
txt_less_output.setText(finalCount + " times(s)");
}
if (values <values_Less){
int finalCount = values_Less - values ;
txt_less_output.setText(finalCount + " times(s)");
}
updateSuff();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
//SUFFICIENT
private void updateSuff() {
DatabaseReference ref = FirebaseDatabase.getInstance().getReference("History");
DatabaseReference mRef = ref.child(FirebaseAuth.getInstance().getCurrentUser().getUid());
Query mQuery = mRef.orderByChild("status").equalTo("SUFFICIENT").limitToFirst(7);
mQuery.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String value = String.valueOf(dataSnapshot.getChildrenCount());
int suffValue = Integer.parseInt(value);
if (suffValue == 0) {
txt_sufficient_output.setText(suffValue + " times(s)");
}
if (suffValue != 0){
txt_sufficient_output.setText(suffValue + " times(s)");
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
Here is the output:
credit to this post that helped me a lot, and thanks for those who tried to help me.
I want to read 2 values from Firebase (if exist as in first user there are not) and if needed to update them. Actually i m trying at first to do it with one value, with no luck. My code is
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
final String uid = Objects.requireNonNull( mAuth.getCurrentUser() ).getUid();
mScoreReference.child( uid ).addListenerForSingleValueEvent( new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
User oldscore = dataSnapshot.getValue( User.class );
if (dataSnapshot.exists()) {
if(oldscore.getScore()==null){
oldscore.setScore(String.valueOf( 0));
String oldscorevalue = Objects.requireNonNull(oldscore).getScore();
int convertedscore = Integer.parseInt(oldscorevalue);
if (convertedscore > 0) {
//Upload points to Database
mScoreReference.child(uid).child( "Score" )
.setValue( convertedscore + newScore );
} else mScoreReference.child(uid).child( "Score" ).setValue( newScore );
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
} );
I managed to convert null(the first value as score is not exist) and to set score. So my problem is why i cant update the value, and how can i update 2 values at the same time? Probably i have to use Transaction, but i m not familiar at all with this. I m reading, but i cant find how to convert this code to Transaction.
UPDATE
I tried with Transaction.
mScoreReference.child( uid ).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
User user = mutableData.getValue(User.class);
if (user.getScore() == null) {
mScoreReference.child(uid).child( "Score" ).setValue( newScore );
}
else{
String oldscorevalue = Objects.requireNonNull(user).getScore();
int convertedscore = Integer.parseInt(oldscorevalue);
mScoreReference.child(uid).child( "Score" )
.setValue( convertedscore + newScore );
}
// Set value and report transaction success
mutableData.setValue(user);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
and i have strange results. At first time do nothing, then adds Score and another irelavent User value, and after this adds 2 Score fields in Database.
I have a db like this :
And I want to update value of cityIdealScore to "cityTop*10 + cityWin" in firebase DB automatically but I don't know how to do.
p.s. I need value of cityIealScore to query DB by Score
p.s.2
public void fbWinUpdate(int cityNum) {
String cNum = String.valueOf(cityNum);
databaseReference.child(cNum).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
CityDetails cd = mutableData.getValue(CityDetails.class);
if(cd == null) {
return Transaction.success(mutableData);
}
cd.cityWin = cd.cityWin + 1;
mutableData.setValue(cd);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
Log.d("DBError", "postTransaction:onComplete:" + databaseError);
}
});
}
public void fbTopUpdate(int cityNum) {
String cNum = String.valueOf(cityNum);
databaseReference.child(cNum).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
CityDetails cd = mutableData.getValue(CityDetails.class);
if(cd == null) {
return Transaction.success(mutableData);
}
cd.cityTop = cd.cityTop + 1;
mutableData.setValue(cd);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
Log.d("DBError", "postTransaction:onComplete:" + databaseError);
}
});
}
public void fbTop4Update(int cityNum) {
String cNum = String.valueOf(cityNum);
databaseReference.child(cNum).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
CityDetails cd = mutableData.getValue(CityDetails.class);
if(cd == null) {
return Transaction.success(mutableData);
}
cd.cityTop4 = cd.cityTop4 + 1;
mutableData.setValue(cd);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
Log.d("DBError", "postTransaction:onComplete:" + databaseError);
}
}); }
It's my cityTop, cityWin, cityTop4's update code. I think I can calculate Score in this method, but I think it's ineffective. I wonder that I can calculate Score at FirebaseRules and so on
This is how you can do it:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference ref = rootRef.child("City_Details_Database");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot ds : dataSnapshot.getChildren()) {
Integer cityTop = ds.child("cityTop").getValue(Integer.class);
Integer cityWin = ds.child("cityWin").getValue(Integer.class);
Integer cityIdealScore = cityTop*10 + cityWin;
dataSnapshot.child("cityIdealScore").getRef().setValue(cityIdealScore);
Log.d("TAG", valueToUpadate);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
ref.addListenerForSingleValueEvent(eventListener);
I have a problem with my Firebase project. I am trying to add a reaction button to it, so what I did is everytime a user presses love/like button, the firebase node which has "ReactingUser" as node, it will add the userID as Key and "1" as value. Also, the total number of reactions on the post is denoted by "TotalReactions" node which will count the total number of children of "ReactingUser" and set the value accordingly. My question is that, what if two user press it at the same time, I know it will cause collision and I may face problem because my data is depended on that "TotalReactions" node. Can anyone tell me how can I use transactions on my database so that I won't have collision problem? I tried going through other tutorials, but none of them clarify me. This is how my code for reaction button looks like:
reactionUserDatabase.child(uniqueKey).child("ReactingUser").child(currentUserID).setValue("1").addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
reactionUserDatabase.child(uniqueKey).child("ReactingUser").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String totalVotes = String.valueOf(dataSnapshot.getChildrenCount());
reactionUserDatabase.child(uniqueKey).child("TotalReactions").setValue(totalVotes).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
} else {
Toast.makeText(context, "Some Error Occured", Toast.LENGTH_LONG).show();
}
}
});
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
Now how can I implement the data transactions to prevent collision? Or am I going a good go and it won't effect ?
This is how you can use transactions:
reactionUserDatabase.child(uniqueKey).child("ReactingUser").child(currentUserID).runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
String stringTotalVotes = mutableData.getValue(String.class);
int totalVotes = Integer.valueOf(stringTotalVotes);
if (totalVotes == null) {
return Transaction.success(mutableData);
}
if (totalVotes != null) {
totalVotes = totalVotes - 1;
} else {
totalVotes = totalVotes + 1;
}
mutableData.setValue(String.valueOf(totalVotes));
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
Log.d(TAG, "postTransaction:onComplete:" + databaseError);
}
});
As you see, this code is using the String class but I strongly recommend you to set the value of your currentUserID field as an int and not as a String.
I'm confused here and really at the end on the line, I have set up a like function in my project. if a user presses the like button once, the like counter update from 0 to 1(liked) and the like imageButton(change color) updates successful. if pressed twice the counter updates from 1 to 0(unlike) successful.
The problem is when a different user also press the like button to like the same post, the like counter does not update from 1 to 2. Please help. I hope this is clear. Below is the code.
viewHolder.mLikebtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mProcessLike = true;
mDatabaseLikeCount = FirebaseDatabase.getInstance().getReference().child("Notes").child(post_key).child("likecount");
mDatabaseLikeCount.keepSynced(true);
mDatabaseLike.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (mProcessLike) {
if (dataSnapshot.child(post_key).hasChild(auth.getCurrentUser().getUid())) {
Log.i("D Diary", "User has already Liked. So it can be considered as Unliked.");
mDatabaseLike.child(post_key).child(auth.getCurrentUser().getUid()).removeValue();
mDatabaseLikeCount.setValue(likeCount = likeCount - 1 );
mProcessLike = false;
} else {
Log.i("D Diary", "User Liked");
mDatabaseLike.child(post_key).child(auth.getCurrentUser().getUid()).setValue(auth.getCurrentUser().getDisplayName());
mDatabaseLikeCount.setValue(likeCount = likeCount + 1 );
Log.i(dataSnapshot.getKey(), dataSnapshot.getChildrenCount() + "Count");
mProcessLike = false;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
});
This should work. You should use a transaction to increment or reduce a value that multiple people will be interacting with.
if (dataSnapshot.child(post_key).hasChild(auth.getCurrentUser().getUid())) {
Log.i("D Diary", "User has already Liked. So it can be considered as Unliked.");
mDatabaseLike.child(post_key).child(auth.getCurrentUser().getUid()).removeValue();
updateCounter(false);
mProcessLike = false;
} else {
Log.i("D Diary", "User Liked");
mDatabaseLike.child(post_key).child(auth.getCurrentUser().getUid()).setValue(auth.getCurrentUser().getDisplayName());
updateCounter(true)
Log.i(dataSnapshot.getKey(), dataSnapshot.getChildrenCount() + "Count");
mProcessLike = false;
}
With updateCounter:
private void updateCounter(bool increment) {
mDatabaseLikeCount.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
if (mutableData.getValue() != null) {
int value = mutableData.getValue(Integer.class);
if(increment) {
value++;
} else {
value--;
}
mutableData.setValue(value);
}
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
// Transaction completed
Log.d(TAG, "likeTransaction:onComplete:" + databaseError);
}
});
}
Firebase Transactions