Android - Firebase Database - android

My application was token generation mobile application. Iam using Firebase to store my data. The token were generated with the help of childrens count in firebase database. The problem I faced was if two users book their appointment at same time then both the users get same token number. For example, the number of childrens in firebase was 5. If the users A and B book their appointment then both the users get the token number 6. But the other user C book the appointment(after A and B) get the correct token number 8.
My code:
int ct=1;
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot dataSnapshot1 : dataSnapshot.getChildren()) {
++ct;
Log.d("log", "onDataChange: " + dataSnapshot1.child("Appointment").child("artistName").getValue());
}
}
The variable ct represents the token number.
What was the solution to solve the above problem?

The token should not be generated based on children count under a node. It will not serve the purpose of identifying a token uniquely in your database.
As far as I could understand your problem, you want to provide a token to the users of your application for an automated appointments to some artists. In this case, I would like to suggest you to take another table in your firebase database which will store all the tokens. Tag the token number along with the artist.
So the final table structure will be something like this.
Appointment
-- Artists
-- TokenNumber
Token
--TokenNumber
If you are familiar with the relational database, you might consider thinking of your token number as a foreign key of another table which is incremented automatically each time a token is assigned to some appointment.
Hope that helps!
Update
Based on the comment, I have found a nice answer in stackoverflow regarding this. Please have a look at the answer on how to increment a value in Firebase. The key idea is to setting the value from client side with a transactional mode. I am sharing the sample code from the answer referred.
public void incrementCounter() {
firebase.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue(1);
} else {
currentData.setValue((Long) currentData.getValue() + 1);
}
return Transaction.success(currentData);
}
#Override
public void onComplete(FirebaseError firebaseError, boolean committed, DataSnapshot currentData) {
if (firebaseError != null) {
Log.d("Firebase counter increment failed.");
} else {
Log.d("Firebase counter increment succeeded.");
}
}
});
}

Related

Firebase rule to only allow one update in android studio

I'm creating an android studio voting application. It is using recyclerview to render candidates information from the database. Once the voter clicks on a vote button, the candidate is added a vote on the firebase realtime database.
I wanted to make sure that a voter can only vote once. Is there a firebase rule I can use or do I have to do it in code?
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
holder.name.setText(candidates.get(position).getFirstname());
holder.party.setText(candidates.get(position).getParty());
holder.category.setText(candidates.get(position).getCategory());
Picasso.get().load(candidates.get(position).getImageurl()).into(holder.profilepic);
holder.vote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
updateTotalVotes("increaseTotalVotes", candidates.get(position).getImageurl());
}
});
}
public static void updateTotalVotes(final String operation, String key) {
System.out.println("Inside updateTotalVotes");
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference totalVotesRef = rootRef.child("candidates").child(key).child("totalVotes");
totalVotesRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
System.out.println("Inside Transactions");
Integer votes = mutableData.getValue(Integer.class);
if (votes == null) {
System.out.println("Inside first if statement = null");
return Transaction.success(mutableData);
}
if (operation.equals("increaseTotalVotes")) {
System.out.println("Inside update Votes by adding 1");
mutableData.setValue(votes + 1);
} else if (operation.equals("decreaseTotalVotes")){
mutableData.setValue(votes - 1);
}
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
// Log.d(TAG, databaseError.getMessage()); //Don't ignore errors!
}
});
}
Firebase's security rules cannot enforce unique value in a specific property under a single node. But (as is often the case with NoSQL databases) you can use a specific data model to implement the use-case.
The usual solution for this is to use the UID of the voter as the key.
votes
uid1: "candidate A"
uid2: "candidate B"
uid3: "candidate A"
Since keys must be unique in a JSON object, this structure ensures by definition that each UID can only vote once.
This is separate from keeping the total votes for a candidate. For that you can either use security rules, or Cloud Functions.
Doing this is in security is appealing, since it means you won't need any server-side code. But the rules can become quite complex. For an example of this, see my answer to this question: Is the way the Firebase database quickstart handles counts secure?
The simpler, and these days more common, approach is to do this with a Cloud Function. From a recent project I worked on, I have this Cloud Function:
exports.countVote = functions.database.ref('/votes/{uid}').onCreate((snapshot, context) => {
let value = snapshot.val();
let countRef = snapshot.ref.parent.parent.parent.child(`totals/${value}`);
return countRef.transaction(function(current) {
return (current || 0) + 1;
})
});
So this tallies the votes for each unique value. It then ensures that users can't change their existing vote with:
{
"rules": {
"votes": {
"$uid": {
".write": "auth.uid === $uid && !data.exists()"
}
}
}
}
So a user can only vote if they user their own UID (the auth.uid variable is prepopulated and can't be spoofed), and if they haven't voted yet.
Short answer is No, you can't by the rules of db.
But you can do it with help of authentication, which user can make an account and his vote record in a sub tree of the candidate. So that the number of sub tree children is the number of votes for this candidate.
Notice: my solution could be broken if fake accounts were made, my advice using phone authentication too, to approve the account is not fake.

How write user data to firebase database only once?

I want to write the user's data to the database only when it first logs on. I don't want it to be written over and over. I wrote a method for this, but I had a problem because there was more than one value in the for loop. How can I solve this problem?
public void writeFirebase() {
final Users users = new Users();
final List<String> arrayList = new ArrayList<>();
myRef.child("Users").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
arrayList.add(ds.getKey());
}
if (arrayList.size() > 0){
for (String cur : arrayList) {
if (cur.matches(mUser.getUid())) {
} else {
Log.e("preee",""+cur);
if (mUser.getPhotoUrl() != null) {
users.setUserProfilPic(mUser.getPhotoUrl().toString());
}
if (mUser.getDisplayName() != null) {
users.setUserName(mUser.getDisplayName());
}
users.setUserId(mUser.getUid());
users.setUserPremium(false);
users.setUserPremiumDate("free");
users.setUserEmail(mUser.getEmail());
myRef.child("Users").child(mUser.getUid()).setValue(users);
}
}
}else {
users.setUserId(mUser.getUid());
users.setUserPremium(false);
users.setUserEmail(mUser.getEmail());
users.setUserName(mUser.getDisplayName());
users.setUserProfilPic(mUser.getPhotoUrl().toString());
users.setUserPremiumDate("free");
myRef.child("Users").child(mUser.getUid()).setValue(users);
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
if you want to write your user data in database once and prevent from writing again you just need to check if that user is already in your database. (in this answer i consider your concern is your user existence, it can be anything else for example maybe your don't want to let user update his email address if he already entered once. so you just check that if user has entered email before or not.)
for my example(user existence) i provide some links which help you check if user is already in your database.
How to search if a username exist in the given firebase database?
How to search if a username exist in the given firebase database?
How to check if a value exists in firebase database - android
just seach and find if your user is already in your database or not. check this ifs before any operation. if he was't you will allow him to continue.

Limit the number of user in Firebase Realtime database

I'm developing an app. The app must able to show the latest 10 registered user detail from real-time database. That is, It removes any user older than latest 10 users. Is there any way I can do this? Right now my app is able to access the user details stored in realtime firebase.
Thanks in advance.
That sounds totally feasible. An incredibly simple way is to retrieve 11 users in your app, and then just remove the last one.
ref.orderByChild("descending_timestamp").limitToFirst(11).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
int userCount = 0;
for (DataSnapshot userSnapshot: dataSnapshot.getChildren()) {
if (userCount++ > 10) {
userSnapshot.getRef().remove();
} else {
// TODO: show the user in your app
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "load users", databaseError.toException());
}
});
You'll note that I order on descending_timestamp, which is a property that you must add to the data and that allows you to sort the users in reverse chronological order. For more on this, see Firebase Data Desc Sorting in Android

Firebase iterations are slow

I'm using firebase with android to create a simple chat app. When the user chooses another user to chat with I want to check whether they've chatted together or not.
In onCreate() method I'm retrieving all the rooms that the current user used before, and I'm putting them in an arraylist called MyChatRooms<>.
Then I want to check each room to see the users of the room.
The problem is that the loop I'm using to iterate through rooms name is finishing before I'm able to retrieve any data from the database.
I know there's similar questions to mine, but none of the answers worked for me.
Here's the related code:
if (!MYChatRooms.isEmpty()) {
for (j = 0; j < MYChatRooms.size(); j++) {
roomref.child(MYChatRooms.get(j)).child("First User").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot1) {
if (!dataSnapshot1.getValue().toString().equals(Username) && dataSnapshot1.getValue().toString().equals(NUsername)) {
Users += dataSnapshot1.getValue().toString() + ",,, ";
} else if (dataSnapshot1.getValue().toString().equals(Username)) {
roomref.child(MYChatRooms.get(j)).child("Second User").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot2) {
if (dataSnapshot2.getValue().toString().equals(NUsername)) {
Users += dataSnapshot2.getValue().toString() + ",,, ";
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
I would suggest that you change the structure of your data. Imagine if a user has 100 chats that means your have to query 200 times to Firebase that of course does not look feasible.
What i would suggest is that your add a recentChat list in every user and whenever a user starts a new chat with someone you add the id of the second user to that list. That way you can track easily with whom the current user has interacted with.
It structure in firebase can look something like this:
User
recentChats
id of the other user
Try to change you database hierarchy or use firestore instead of real time database
Please check the following topic: https://firebase.google.com/docs/database/usage/optimize?
In my case, I had added and index column and limited the query in Firebase Rules.

How to pull specific value from firebase

I am working on the app where I need to pull specific value from the firebase. For this case I need to pull "Score" where "uID" equals current User id. What would be a best way to do so.
Thank you for any help.
Firebase will always return full nodes. So you cannot just return the score for a user. But you can return the common node that both the score and user are under (the one starting with -K...) by using a Firebase query:
DatabaseReference leadersRef = FirebaseDatabase.getInstance().getReference("Leaders");
Query query = leadersRef.orderByChild("uID").equalTo("vUdnKx...");
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
for (DataSnapshot child: snapshot.getChildren()) {
System.out.println(child.getKey()+": "+child.child("Score").getValue(Long.class));
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "onCancelled", databaseError.toException());
// ...
}
})
Note that the code loops over the snapshot in the result. That is because a query will potentially have multiple results. So the snapshot contains a list of those results. Even if there is only a single result, the snapshot will contain a list of one result.
For more reading see the Firebase documentation on handling lists and sorting and filtering data.

Categories

Resources