Ibrahim Abousalem Ibrahim Abousalem - 2 months ago 18
Android Question

How to know which user is trying to Sign-in?

enter image description here

Users are created using Email and Password. This is how I do the Sign-up:

mSignup.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

mEmailStr = removeSpaces(mEmail.getText().toString());
mPasswordStr = mPassword.getText().toString();
mUsernameStr = mUsername.getText().toString();
mIsSgl = mSglCheckBox.isChecked();
mUsernameStr=mUsername.getText().toString();
final User mUser = new User();
mUser.setEmail(mEmailStr);
mUser.setPassword(mPasswordStr);
mUser.setIsSgl(mIsSgl);
mUser.setStudyGroupName(mStudyGroupName);
mUser.setUsername(mUsernameStr);
FirebaseAuth.getInstance().createUserWithEmailAndPassword(mUser.getEmail(), mUser.getPassword()).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(Task<AuthResult> task) {


if (task.isSuccessful()) {
Toast.makeText(getActivity(), "Sucsses", Toast.LENGTH_SHORT).show();
generateUser(mUser);
startActivity(new Intent(getActivity(), MainActivity.class));
} else {
Toast.makeText(getActivity(), "not Sucsses", Toast.LENGTH_SHORT).show();
}
}
});



}
});


This is how I push the data into database:

public void generateUser(User user)
{

FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference users;
if(user.getIsSgl())
{
users = database.getReference(user.getStudyGroupName()).child("SGL");
}
else
{
users = database.getReference(user.getStudyGroupName()).child("Student");
}

users.push().setValue(user);

}


This is how I Sign-in:

mSignin.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {

mEmailStr = SignupActivityFragment.removeSpaces(mEmail.getText().toString());
mPasswordStr = mPassword.getText().toString();

mAuth.signInWithEmailAndPassword(mEmailStr, mPasswordStr).addOnCompleteListener(getActivity(), new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(Task<AuthResult> task) {
if (task.isSuccessful()){


FirebaseDatabase database = FirebaseDatabase.getInstance();
// thats not worked for me
database.getReference("StudyGroups").child("Student").orderByChild("email").equalTo(mEmailStr).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot childSnapShot : dataSnapshot.getChildren()) {
userKey = childSnapShot.getKey();
}

Toast.makeText(getContext(),"Userkey: " + userKey,Toast.LENGTH_LONG).show();
Log.v("himaAbousalem",userKey);
}

@Override
public void onCancelled(DatabaseError databaseError) {

}
});
Toast.makeText(getActivity(), "Sucsses ", Toast.LENGTH_SHORT).show();
startActivity (new Intent(getActivity(),Controller.class));
}else {
Toast.makeText(getActivity(), "not sucsses", Toast.LENGTH_SHORT).show();
}

}
});
}
});


I want to query the database so that when a user signs-in by Email and password, it returns all the data of that user from the database.

How can I make the key of
userId
in Auth equal to the
userId
in database and how do I use that feature?

Answer

tl;dr - In this case, store each user using their associated uid generated by Fireabase Auth instead of using a push ID.


In your situation the challenge with using a push ID to store info specific to each user is that when a user signs in you don't know the push ID you used when you first stored their info. To find a user each time they sign in you would have to search through every user in your database until you find a matching email/password to retrieve their correct profile information - the more users you have, the longer it would take to do the search. One alternative, which would probably be faster, is to use Firebase Authentication to create users and the Firebase Database to store any user specific information.

When you create a new user using Firebase Authentication it will assign a unique user id to the user that will be the same throughout the lifetime of the user. You then use the unique user id generated by Firebase Auth instead of a push ID to store user info in the database. The next time a user signs in you get the user's uid from Firebase Auth and use it to query the database to get that user's information.

Check the guide for how to create a password-based user and how to sign a user in using Firebase Auth: Create a password-based account

In order to use the unique uid generated by Firebase Auth I suggest a few changes to your code.

Update database structure

I suggest you update your database structure by adding a new location (maybe "allUsers") for use when you create/sign in users. Right now it looks like your are breaking up students into groups. If you need to keep this structure, for reasons beyond authentication, you can use it along with my suggestion. The reason for a single location which stores all users is that you need a definite location to query when a user signs in. When using Firebase Auth, without a single location which stores all users there is no way to tell what group a user belongs to when they first sign in. You would have to check every group in order to find a match and that may take a long time. Having a location which stores all users solves that problem. Also, the query for retrieving user information from that single location is much simpler. If you do need to keep a user's information in multiple places just be sure to update their information in both places if any changes occur.

Create a class variable used to distinguish between create user and sign in existing user.

If you use the same Activity to create a new user and sign in an existing user then create a boolean variable to make a distinction between when a new user is being created and when an existing user is signing in. It will be used later in the AuthStateListener. If you handle user creation in a separate activity from general sign in then you shouldn't need this variable because each activity would have a separate AuthStateListener.

boolean mIsNewUser = false; 

Move the call to generateUser() from the create user completion listener to an AuthStateListener. Also move your database query from the sign in completion listener to the AuthStateLisener

Whenever you create a user successfully they will automatically be signed in too. So, if you move your call to generateUser() from the createUserWithEmailAndPassword OnCompleteListener to your AuthStateListener you can get access to the created user's uid. When signing an existing user move your database query to the AuthStateListener as well, again so we can access the user's uid. I'm also going to create a helper method for the database query called getExistingUserInfo. As an FYI, the onComplete() callback in the OnCompleteListeners for creating and signing in users gives you access to an AuthResult which according to the API has a method for returning the current user but the documentation says to access user information in the AuthStateListener.

private FirebaseAuth mAuth;
private FirebaseAuth.AuthStateListener mAuthListener;

@Override
protected void onCreate(Bundle savedInstanceState) {
    mAuth = FirebaseAuth.getInstance();
    mAuthListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
                FirebaseUser user = firebaseAuth.getCurrentUser();
                if (user != null) {
                    // User is signed in
                    Log.d(TAG, "onAuthStateChanged:signed_in:" + user.getUid());

                    if(mIsNewUser){
                        //call to generate user using Uid instead of pushID
                        generateUser(mUser, user.getUid());
                    } else{
                        getExistingUserInfo(user.getUid());
                    }
                    startActivity(new Intent(getActivity(), MainActivity.class));
                } else {
                    // User is signed out
                    Log.d(TAG, "onAuthStateChanged:signed_out");
                }
                // ...
            }
        };
        // ...
    }
}

@Override
public void onStart() {
    super.onStart();
    mAuth.addAuthStateListener(mAuthListener);
}

@Override
public void onStop() {
    super.onStop();
    if (mAuthListener != null) {
        mAuth.removeAuthStateListener(mAuthListener);
    }
}

Update your generateUser() helper method to use the uid instead of a push ID:

I'm going to assume you want to keep you existing database structure and add the single location for all users as suggested. Based on this I've made a couple of changes to the write operation you were using in generateUser(). Mainly, instead of using setValue() to write to the database I'm using updateChildren(). By using updateChildren() we can take advantage of Firebase's ability to do atomic updates. This will allow us to write to the appropriate student group location and the location storing all users simultaneously. By taking this approach if the write operation to either location fails neither location will be updated. This way you can be certain if a student is added to a group they will also be listed in the allUsers location.

public void generateUser(User user, String uid)
  {

        FirebaseDatabase database = FirebaseDatabase.getInstance().getReference();
        String userType;
        String allusers = "allUsers/" + uid;
        Map(String, Object) newUserUpdate = new HashMap<>();
        if(user.getIsSgl())
        {
            userType = user.getStudyGroupName() + "/" + "SGL";
        }
        else
        {
            userType = user.getStudyGroupName() + "/" + "Student";
        }
        newUserUpdate.put(userType, user);
        newUserUpdate.put(allusers, user);
        database.updateChildren(newUserUpdate);      
    }

Update database query to use new location which stores all users

As I mentioned above, by creating a single location for all users you can reduce the complexity of the query used to find a user's info when they sign in. Again, if you need to store users by group you can keep that but be sure to update both locations if a users info changes.

public void getExistingUserInfo(String uid){

 FirebaseDatabase database = FirebaseDatabase.getInstance(); 
                        database.getReference("allUsers").child(uid).addListenerForSingleValueEvent(new ValueEventListener() {
                            @Override
                            public void onDataChange(DataSnapshot dataSnapshot) {
                                //get user data from dataSnapshot

                                Toast.makeText(getContext(),"Userkey: " + userKey,Toast.LENGTH_LONG).show();
                                Log.v("himaAbousalem",userKey);
                            }

                            @Override
                            public void onCancelled(DatabaseError databaseError) {

                            }
                        });

}

Set the variable used to distinguish between existing user sign in and new user creation in the create user completion listener

FirebaseAuth.getInstance().createUserWithEmailAndPassword(mUser.getEmail(), mUser.getPassword()).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(Task<AuthResult> task) {

                    if (task.isSuccessful()) {

                        //set boolean used in the AuthListener
                        mIsNewUser = true;
                        Toast.makeText(getActivity(), "Sucsses", Toast.LENGTH_SHORT).show();

                    } else {
                        Toast.makeText(getActivity(), "not Sucsses", Toast.LENGTH_SHORT).show();
                    }
                }
            });
Comments