When the user logon with your google account for the first time in the app, I made this activity to create the user in the database. It's working, but are creating more than one user in the database, and I don't know why.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_account);
mAuth = FirebaseAuth.getInstance();
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
if (firebaseAuth.getCurrentUser() == null) {
goLogInScreen();
} else {
final String userGoogleEmail = firebaseAuth.getCurrentUser().getEmail();
databaseUser.orderByChild("userEmail").equalTo(userGoogleEmail).addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//User already exists
} else {
//Can create new user
String id = databaseUser.push().getKey();
User user = new User(id, userGoogleEmail, null);
databaseUser.child(id).setValue(user);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
};
This is happening because your are using as an identifier the key generated by the push() method and not another identifier and this is actually creating the user again, even if exists in your database.
What i'm recomending you to do, is to change a little bit the logic of saving the data in your database. So in order to solve your problem, i recomand you using in stead of that key, the email address as an identifier, because is also unique. Your database should look like this:
Firebase-database
|
--- Users
|
--- jon#email,com
| |
| --- //data
|
--- jack#email,com
|
--- //data
As you probably see, i have saved the email addreees in the Firebase database using , (comma) and not . (dot).
name#email.com -> name#email,com
This is because Firebase does not allow symbols like . in the key name. So to store the values like this, the encoded email is required. To achieve this, i recomand you using the following methods:
String encodeUserEmail(String userEmail) {
return userEmail.replace(".", ",");
}
String decodeUserEmail(String userEmail) {
return userEmail.replace(",", ".");
}
To verify if a user exists, simply put the a listeenr on the Users node and use exists() method on the dataSnapshot object like this:
DatabaseReference usersRef = usersDatabaseReference.child(userEmail);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (!dataSnapshot.exists()) {
//create user
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
usersRef.addListenerForSingleValueEvent(valueEventListener);
Hope this solves your problem.
Related
This question already has answers here:
Checking if a particular value exists in the Firebase database
(6 answers)
Closed 2 years ago.
In my app I have to verify that there are not more than two users with the same name. I tried it with the following code, but when it detects that the user who tries to register already exists, it appears a toast that indicates to change it.
The problem is that even if you know that name already exists and although the toast appears, the account is created without importing the imposed condition, thus creating two users with the same username.
How could I avoid that, that two users with the same name will be created?
regBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
regBtn.setVisibility(View.INVISIBLE);
loadingProgress.setVisibility(View.VISIBLE);
final String email = userEmail.getText().toString();
final String password = userPassword.getText().toString();
final String password2 = userPAssword2.getText().toString();
final String name = userName.getText().toString();
if(verificationUsername(name)){
showMessage("ass");
regBtn.setVisibility(View.VISIBLE);
loadingProgress.setVisibility(View.INVISIBLE);
}
public boolean verificationUsername (String equalto){
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference.child("Users").orderByChild("username").equalTo(equalto);
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String TypeUser = dataSnapshot.child("username").getValue().toString();
if (dataSnapshot.exists()) {
System.out.println(TypeUser + "dssssssssssssssssssssssssddddddsssssssssssssssssssssss");
lola = dataSnapshot.toString();
showMessage("El nombre de usuario ya esta en uso, intenta con uno nuevo");
// dataSnapshot is the "issue" node with all children with id 0
for (DataSnapshot issue : dataSnapshot.getChildren()) {
// do something with the individual "issues"
}
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
return false;
}
First of all I would recommend you to store the usernameUpperCase for all sentences become uppercase because Firebase Realtime is case sensitive.
So example for database something look like this:
username: Ticherhaz
usernameUpperCase: TICHERHAZ
I would prefer to use addListenerForSingleValueEvent because we want to read for once only.
public boolean verificationUsername (String equalto){
DatabaseReference reference = FirebaseDatabase.getInstance().getReference();
Query query = reference.child("Users").orderByChild("usernameUpperCase").equalTo(equalto.toUpperCase());
query.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()) {
//that's mean this username already exist
}
else {
//so username not exist yet, we create here new username.
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
return false;
}
I'm trying to set up an order placing system.
Once the user is verified via email, it can create/update/delete order.
Order is saved into Firebase Real-time Database and users are saved in Authentication.
I would like to allow user to only see/edit orders that were placed by this specific user. Basically make use of UserUID from Authentication section.
public class FirebaseDatabaseHelper {
private FirebaseDatabase mDatabase;
private DatabaseReference mReferenceOrders;
private List<Order> orders = new ArrayList<>();
public interface DataStatus{
void DataIsLoaded(List<Order> orders, List<String> keys);
void DataIsInserted();
void DataIsUpdated();
void DataIsDeleted();
}
//Initialize Database object
public FirebaseDatabaseHelper() {
mDatabase = FirebaseDatabase.getInstance();
mReferenceOrders = ((FirebaseDatabase) mDatabase).getReference("order");
}
public void readOrders(final DataStatus dataStatus){
mReferenceOrders.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
orders.clear();
List<String> keys = new ArrayList<>();
for(DataSnapshot keyNode : dataSnapshot.getChildren()) {
keys.add(keyNode.getKey());
Order order = keyNode.getValue(Order.class);
orders.add(order);
}
dataStatus.DataIsLoaded(orders,keys);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
public void addOrder(Order order, final DataStatus dataStatus) {
String key = mReferenceOrders.push().getKey();
mReferenceOrders.child(key).setValue(order).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
dataStatus.DataIsInserted();
}
});
}
// Update and Delete methods
public void updateOrder(String key, Order order, final DataStatus dataStatus){
mReferenceOrders.child(key).setValue(order).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
dataStatus.DataIsUpdated();
}
});
}
public void deleteOrder(String key, final DataStatus dataStatus){
mReferenceOrders.child(key).setValue(null).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
dataStatus.DataIsDeleted();
}
});
}
}
Here's what I was thinking:
When a user creates a new order then a UserUID is added to a database and nested inside 'order' so now each order can be assigned to a user that created it.
Now the next step would be to display this order to a user that created it but only if cust_id (in order) matches UserUID of a logged in user. Would that be a good approach?
Yes, it is a good approach, now you need to add to your Firebase Realtime Database now branch with your users data based on userUID, like this:
Thanks to this you will be able to connect your users with their orders data and besides you can save here more specific user data like "how many orders user create", "how many orders are active" etc.
I have added a user_id to an 'order' in the Firebase database so each order can be assigned to its user.
I got the parameter for user_id by fetching a UserUID from an Authentication section of Firebase when a new user is logged/signed in.
Screenshot of UserUID in Authentication section of Firebase
I got this value in my code by adding the following:
private FirebaseAuth mAuth;
order.setUser_id(mAuth.getCurrentUser().getUid());
Once I got the user_id assigned to each order I've created a following if statement which is implemented in my readOrders function which you can see in my original post above:
public void readOrders(final DataStatus dataStatus){
mReferenceOrders.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
orders.clear();
List<String> keys = new ArrayList<>();
for(DataSnapshot keyNode : dataSnapshot.getChildren()) {
keys.add(keyNode.getKey());
Order order = keyNode.getValue(Order.class);
**if (order.getUser_id().equals(mAuth.getUid())) {
Log.d("FirebaseDatabaseHelper", "match");
orders.add(order);
}else {
Log.d("FirebaseDatabaseHelper", "error");
}**
}
dataStatus.DataIsLoaded(orders,keys);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
So basically now when the new user logs in, there will be nothing to show because the user_id won't match with any of the user_id's inside Order database.
I'm not sure how efficient this method will be when more users/orders will be added so I'll have to do some testing.
Please advise if this is the best approach!
This is pretty direct forward but I have a problem with multiple users.When the first user signs in,his name and email are fetched with no issues but when he signs out and another user signs in (same phone), current user details return null.
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
String uid = Objects.requireNonNull(firebaseAuth.getCurrentUser()).getUid();
userDetails = FirebaseDatabase.getInstance().getReference().child("Users").child(uid);
myref.keepSynced(true);
userDetails.keepSynced(true);
name = view.findViewById(R.id.my_name);
mail = view.findViewById(R.id.my_mail);
userDetails.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
String Name = dataSnapshot.child("name").getValue().toString();
String Email = dataSnapshot.child("email").getValue().toString();
name.setText(Name);
mail.setText(Email);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
Turns out the code is fine, only problem is my firebase structure. The first user "email" and "name" are in lowercase, the rest start with a capital letter users node, firebase Image
I am working on an application similar to tinder but to help users to find people to play a specific sport with.
I currently have the code searching the database for the gender of the users (that the user can be matched with). However each user in the database has a node that contains all of the sports the user can pick. If the user prefers a sport the value is saved as 'true' and if not, the value is saved as 'false'. The appropriate users are then shown on the app.
A screenshot of the database is shown below:
This is the code I have so far:
public void checkUserSex(){
final FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
DatabaseReference userDb = usersDb.child(user.getUid());
userDb.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.exists()){
if (dataSnapshot.child("sex"). getValue() != null){
userSex = dataSnapshot.child("sex").getValue().toString();
switch (userSex){
case "Male":
oppositeUserSex = "Female";
break;
case "Female":
oppositeUserSex = "Male";
break;
}
getOppositeSexUsers();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
public void getOppositeSexUsers(){
usersDb.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
if (dataSnapshot.child("sex").getValue() != null) {
if (dataSnapshot.exists() && !dataSnapshot.child("connections").child("pass").hasChild(currentUId) && !dataSnapshot.child("connections").child("play").hasChild(currentUId) && dataSnapshot.child("sex").getValue().toString().equals(oppositeUserSex)) {
String profileImageUrl = "default";
if (!dataSnapshot.child("profileImageUrl").getValue().equals("default")) {
profileImageUrl = dataSnapshot.child("profileImageUrl").getValue().toString();
}
cards item = new cards(dataSnapshot.getKey(), dataSnapshot.child("name").getValue().toString(), profileImageUrl);
rowItems.add(item);
arrayAdapter.notifyDataSetChanged();
}
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
How do I change it from matching gender to matching the selected sport?
Unfortunately, Firebase Realtime database does not allow a query based on multiple properties. To achieve what you want, you don't need to restructure your database entirely, you just need to change it a little bit. To solve this problem, you need to add a new node for each sport. Every time you add a new user which plays golf, add it also in its corresponding sport node. Your new node should look like this:
Firebase-root
|
--- golfPlayers
|
--- userId1 : true
|
--- userId2 : true
With this structure you can query your database to get only the users who are playing golf. This can be done by attaching a listener on golf node and iterate on the DataSnapshot object.
This practice is called denormalization and is a common practice when it comes to Firebase. For a better understanding, I recommend you see this video, Denormalization is normal with the Firebase Database.
Note, what you are trying to do and cannot be solved using Firebase Realtime database, can be solved using Cloud Firestore. Give it a try.
I have the following JSON structure in my Firebase Database:
- users
|
+--- key: 123
| |
| +-----name : Tom
| +-----email: tom#mymail.com
|
+--- key: 456
|
+-----name : Peter
+-----email: peter#othermail.com
Now I want to check if any user with the email tom#mymail.com exists or not.
I thought:
DatabaseReference usersRef = db.getReference("users");
usersRef.orderByChild("email")
.equalTo("tom#mymail.com")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//fires if exists
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
But this only fires if a user exists and not if it doesn't. How to do it properly?
You can use exactly the code you provided. It will fire no matter if it exists or not. You only need to check if the data exists in the onDataChange() method:
DatabaseReference usersRef = db.getReference("users");
usersRef.orderByChild("email")
.equalTo("tom#mymail.com")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
//exists!
}
else {
//does not exist
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I thought this does not work and I could swear I tried it (maybe with an earlier version of Firebase) but it works!
I see two ways of approaching this:
Load the entire list of users and process it on the device (Not recommended if you have a huge number of users, which you probably do). I will not post the code here because I'm affraid beginners will simply copy and paste it and write awful apps...
Create a node containing the emails of users registered (let's call it userEmails):
userEmails
{
"tom#mymail.com":true,
"peter#othermail.com": true
}
Now, in order to check if the user exists or not:
usersRef.child("tom#mymail.com").addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
//User exists
}
else
{
//User doesn't exist
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
});
The downside of this is that you will have to insert data into both nodes when a new user is registered.