How to add a counter in firebase? - java

So guys, I had the database: Event and User.
When some user has interest in some event clicking the button, this will add a child in eventHasInterest with the user in Event database, and in the database User will add the event that has interest. It's already working, but I need to put a counter to show, how many people has interest, and it's not working, only add once. I need one click, +1, another click -1 on.
btn_interest.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
databaseEvent.child(getKeyEvent()).addListenerForSingleValueEvent( //get the event by key
new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
final Event event = dataSnapshot.getValue(Event.class);
user = FirebaseAuth.getInstance().getCurrentUser(); //get the user logged in
if(user != null) {
databaseUser.orderByChild("userEmail").equalTo(user.getEmail()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()) {
final User user = userSnapshot.getValue(User.class); // user data logged in
databaseUser.orderByChild("userHasInterest").equalTo(event.getEventId()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.exists()) {
databaseUser.child(user.getUserId()).child("userHasInterest").child(event.getEventId()).setValue(event.getEventId());
databaseEvent.child(event.getEventId()).child("eventAmount").setValue(dataSnapshot.getChildrenCount()+1);
} else {
//event already exists
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
databaseEvent.orderByChild("eventHasInterest").equalTo(user.getUserId()).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()){
databaseEvent.child(event.getEventId()).child("eventHasInterest").child(user.getUserId()).setValue(user.getUserId());
} else{
//user already exist
}
}
My firebase:

dataSnapshot.getChildrenCount() does not return the int value of eventAmount, it returns the number of children that eventAmount has. In your case, eventAmount will always return 0 since there is no children of eventAmount. I suggest that instead of using getChildrenCount, get the value of the dataSnapshot, and parse that value into an int. After that, increment that value by 1, and store that value instead.
databaseEvent.child(event.getEventId()).child("eventAmount").setValue(Integer.parseInt(dataSnapshot.getValue().toString()) + 1);
EDIT: As suggested by Frank, storing the value using a transaction is recommended to help avoid concurrent updates. I used this post, as well as the post Frank linked to help write the code.
public void updateCount(DatabaseReference database){
database.runTransaction(new Handler() {
#Override
public Result doTransaction(MutableData mutableData) {
//Currently no value in eventAmount
if(mutableData.getValue() == null){
mutableData.setValue(1);
}
else{
mutableData.setValue(Integer.parseInt(mutableData.getValue().toString()) + 1);
}
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b,
DataSnapshot dataSnapshot) {
//Probably log the error here.
}
});
}
So in your "userHasInterest" onDataChange method, call my method above like this.
#Override
public void onDataChange(DataSnapshot dataSnapshot){
if(!dataSnapshot.exists()) {
databaseUser.child(user.getUserId()).child("userHasInterest").child(event.getEventId()).setValue(event.getEventId());
updateCount(databaseEvent.child(event.getEventId()).child("eventAmount")); //New line here
} else {
//event already exists
}
}

Related

How to retrieve push() valued data from Firebase to android

I am going to make a simple app and I am completely new to Android development. I want to develop an edit button to save my data in the Realtime Database. This is my code:
holder.edite.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final DialogPlus dialogPlus=DialogPlus.newDialog(holder.title.getContext())
.setContentHolder(new ViewHolder(R.layout.dialogcontent))
.setExpanded(true,2100)
.create();
View myView=dialogPlus.getHolderView();
EditText title=myView.findViewById(R.id.hTitle);
EditText description=myView.findViewById(R.id.hDescription);
Button submit=myView.findViewById(R.id.usubmit);
title.setText(myItems.getName());
description.setText(myItems.getAddHomeworkDescription());
dialogPlus.show();
submit.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Map<String,Object> map=new HashMap<>();
map.put("name",title.getText().toString());
map.put("addHomeworkDescription",description.getText().toString());
DatabaseReference myRef = getInstance().getReference().child("Homework");
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
// String key = myRef.push().getKey();
myRef.child(uid).child()
.updateChildren(map);
dialogPlus.dismiss();
}
});
}
});
This is how my firebase database looks like
What I want to add into .child() to get highlighted(In the image) Unique id direction. But this unique ID is not always same. It change everythime when user create new one.
You will either have to know the push key (-NN...) value of the node you want to update already, determine it with a query, or loop over all child nodes and update them all.
Update a specific child with a known push key
myRef.child(uid).child("-NNsQO7O9lh0ShefShV")
.updateChildren(map);
Update children matching a specific query
Say that you know you want to update the node with name "test12", you can use a query for that:
myRef.child(uid).orderByChild("name").equalToValue("test12").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot nodeSnapshot: dataSnapshot.getChildren()) {
nodeSnapshot.getRef().updateChildren(map);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
})
Update all children
This is a simpler version of the above, by removing the query condition:
myRef.child(uid).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot nodeSnapshot: dataSnapshot.getChildren()) {
nodeSnapshot.getRef().updateChildren(map);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
throw databaseError.toException();
}
})

How to ensure Completion handler for one Firebase lookup completes before another one does

I have the following Firebase DB node structure:
UserInGroup
--- GroupID
--- UserId : true/false
Users
--- UserId
--- Username : String
--- ...
GroupStatus
--- GroupId
--- UserId: true/false
I need to pull for the first node to get all the users in the Group
Then use that info to get the users account info details
Finally check to see the users status in the Group
I cannot figure a way to implement the completionhandler in Java/Android ? I have done so for iOS with completionhandlers.
Can anyone assist with helping me implement the solution in Java?
---- UPDATE ----
I have done the following:
// Create an interface to init all the callback functions
private interface AllUsersCallback {
void onSuccess(DataSnapshot dataSnapshot);
void onStart();
void onFailure();
}
private void readData(Query query, AllUsersActivity.AllUsersCallback listener) {
listener.onStart();
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
listener.onSuccess(dataSnapshot);
} else { // dataSnapshot doesn't exist
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
//
listener.onFailure();
}
});
}
And lastly the Activity view:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Init ArrayList
userList = new ArrayList<>();
userInGroupReference = mFirebaseDatabase.getReference("GroupUsers");
userInGroupQuery = userInGroupReference.child(groupID).orderByValue().equalTo(true);
// Completion Handler for Lookups
readData(userInGroupQuery, new AllUsersActivity.AllUsersCallback() {
#Override
public void onSuccess(DataSnapshot dataSnapshot) {
// Clear the List (remove dupes)
userList.clear();
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
String userId = snapshot.getKey();
// Call function to set usernames to the users
setUsername(userId);
}
/*
THIS ALWAYS COMES OUT BLANK!? <--------
*/
for (int i = 0; i < userList.size(); i++) {
Log.e(TAG,"List element: " + userList.get(i).getUsername());
}
}
#Override
public void onStart() {
// When starting
Log.d("ONSTART", "Started");
}
#Override
public void onFailure() {
// If failed
Log.d("onFailure", "Failed");
}
});
}
and the function used to set the users username to the userList:
public void setUsername(String userId) {
userReference = mFirebaseDatabase.getReference("Users");
userQuery = userReference.child(userId).child("username");
// Add handle for listener
userQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String username = dataSnapshot.getValue().toString();
AllUsers result = new AllUsers(username);
userList.add(result);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
These database calls are asynchronous - the callback code does not run immediately, it runs some time in the future when you actually get the data.
The easiest way to chain multiple dependent async queries is to put each query into its own function, and call it from the dependent query's callback. In your case, you could have multiple callbacks running at once, so as each one completes you can check for it to be done and check for them all to be done by comparing the size of the list with the number of queries launched.
For example:
private ArrayList<String> userList = new ArrayList<>();
private int numUsers = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// other setup stuff
startInitialQuery();
}
private void startInitialQuery() {
// make your initial query
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
userList.clear();
numUsers = 0; // dataSnapshot.getChildren().size();
// If the size() call above works, use that, otherwise
// you can count the number of children this way.
for(DataSnapshot snap : dataSnapshot.getChildren()) {
++numUsers;
}
for(DataSnapshot snap : dataSnapshot.getChildren()) {
String userId = snap.getKey();
readUser(userId);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
});
}
private void readUser(String userId) {
// make userQuery using "userId" input
userQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String username = dataSnapshot.getValue().toString();
userList.add(username);
checkLoaded();
}
else {
--numUsers;
checkLoaded();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
--numUsers;
checkLoaded();
}
});
}
private void checkLoaded() {
if( userList.size() == numUsers ) {
// All done getting users! Show a toast, update a view, etc...
}
}
Alternately, if you switch to using Kotlin and coroutines you can write this as a pretty simple linear suspend function where you can actually make the different tasks wait.
A cleaner, but more invasive change, would be to move this all to a ViewModel that contains LiveData of each of these steps. As data is received, you post it to the LiveData and the UI can observe that and react accordingly (e.g update views, trigger the next call, etc).
Update
Here is an example showing how to do this with a ViewModel and LiveData
public class MainViewModel extends ViewModel {
private final MutableLiveData<List<String>> users = new MutableLiveData<>();
LiveData<List<String>> getUsers() {
return users;
}
private final ArrayList<String> userList = new ArrayList<>();
void startFetchingData() {
// build query
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
userList.clear();
for(DataSnapshot snap : dataSnapshot.getChildren()) {
String userId = snap.getKey();
readUser(userId);
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
});
}
private void readUser(String userId) {
// build userQuery
userQuery.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
String username = dataSnapshot.getValue().toString();
userList.add(username);
users.postValue(userList);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.d(TAG, databaseError.getMessage());
}
});
}
}
and in the activity you set an observer for the LiveData that is notified any time the data changes.
model = new ViewModelProvider(this).get(MainViewModel.class);
final Observer<List<String>> userObserver = userList -> {
// Update the UI, or call something else
// this will get called every time the list of users is
// updated in the ViewModel
System.out.println("TEST: got data " + userList);
};
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.getUsers().observe(this, userObserver);
model.startFetchingData();

Fetching data from firebase database

I want to fetch data from inside Firebase to check whether this user exists in database but there's a problem that i can't solve , listener trigger late this is my code :-
if I remove while loop i can't fetch object fast
if I keep while loop i enter infinite loop , i don't know why
why listener don't trigger
DataSnapshot fetched ;
public boolean user_exist(final String user) throws Exception {
users.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
fetched = dataSnapshot ;
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
while (fetched == null){
Log.e("dbController","not fetched yet");
}
return fetched.hasChild(user);
}
Firebase has to fetch the data from database and bring it in your app. This may take time, hence it should be done in background. When you add valueEventListener, the fetching is done in background. You may display a progressBar to show data is still loading, and once data is in hands, do the rest of code:
users.child(user).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
funUserExists();
} else {
funNoUser();
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});

Firebase Database getting Objects

so honestly I just feel stupid right now but I just don't get it...
I want to get one "User" Object from my Firebase Realtime Database, so I add an ValueListener, right?
I have a Method "getUser" which has "User" as returnvalue. In there I use this:
ValueEventListener valueListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot snap : dataSnapshot.getChildren())
{
User u = snap.getValue(User.class);
if(u.getEmail().equals(userEmail))
{
//user = u;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
//Adding the Listener for Single Event
fref.child("User").addListenerForSingleValueEvent(valueListener);
//Using "u" here then
Now I don't see a good way get that User "u" out of there, how do I get it?
I know this should be basics, but I just don't get it..
thanks in advance :)
what about:
private User targetUser;
#Override
protected void onStart() {
super.onStart();
final ValueEventListener userListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
userNameList = new ArrayList<>((ArrayList) dataSnapshot.getValue());
for (User u : userNameList) {
if (u.getEmail().equals(userEmail)) {
targetUser = u;
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
Log.e(TAG, "onCancelled: Failed to load User list.");
}
};
userlistReference.addValueEventListener(userListener);
mUserListListener = userListener;
//use targetUser reference but check if it is null(can be null)...
}
You have to use a CountDownLatch - this object will let you wait till the listener is actually invoked before returning the object :
private User getUser() {
final User user = null ;
final CountDownLatch cdl = new CountDownLatch(1);
ValueEventListener valueListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot snap : dataSnapshot.getChildren())
{
User u = snap.getValue(User.class);
if(u.getEmail().equals(userEmail))
{
user = u;
break ;
}
}
cdl.countDown();
}
#Override
public void onCancelled(DatabaseError databaseError) {
cdl.countDown();
}
};
//Adding the Listener for Single Event
fref.child("User").addListenerForSingleValueEvent(valueListener);
try {
cdl.await();
} catch (InterruptedException ie) {
}
return user ;
}
You just need to create global variable on your class, like:
private User myUser;
Then inside your listener just do:
myUser = u;
This is the easiest way...

how to use firebase query in if else condition

here is the code of the onclick of button , what i want to do is on click of his button app must fetch the email from the firebase database if successful it must show up in edit text that email found else show in Edit-text that email not found , here i am able to fetch the email and show that email found on Edit-text but not able to show the email not found (else part of the code ) instead i get this in console
W/PersistentConnection: pc_0 - Using an unspecified index. Consider adding '".indexOn": "email"' at /users/users to your security and Firebase rules for better performance
conbtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Firebase ref = new Firebase("https://(refrence of firebse database)");
final String searchEmail = emailcon.getText().toString().trim();
final Query query = ref.orderByChild("email").equalTo(searchEmail);
query.addValueEventListener(new ValueEventListener()
{
#Override
public void onDataChange(DataSnapshot dataSnapshot)
{
for (DataSnapshot child : dataSnapshot.getChildren())
{
final String fetchEmail;
Map<?, ?> value = (Map<?, ?>) child.getValue();
Log.d("main2activity ","User data : "+ value);
fetchEmail = (String) value.get("email");
Log.d("main2activity ","User email : "+ fetchEmail);
if (searchEmail.equals(fetchEmail))
{
emailcon.setText("email found hurray "+query.getRef());
}
else
{
emailcon.setText("still no email found!!");
}
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
}
});
Since you're firing a query, you will get a snapshot that can contain 0 or more children. If it contains any children, those children will have the email address you used in equalTo()
You need to handle the onDataChange() slightly differently:
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChildren()) {
for (DataSnapshot child : dataSnapshot.getChildren()) {
emailcon.setText("email "+searchEmail+" found at URL "+child.getRef());
}
}
else {
emailcon.setText("still no email found!!");
}
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});

Categories

Resources