0

I have a problem regarding my userNameExistsAlready-method in my Android App.
Basically, I have a Cloud Firestore database with the collection user.
Its documents are only populated with username, password and email (all String). Document IDs are auto-generated. I've created an RegisterActivity which works fine. Here is the onCreate(..) method of my RegisterActivity:

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register);

userNameInput = (TextInputEditText) findViewById(R.id.userNameInput);
emailInput = (TextInputEditText) findViewById(R.id.emailInput);
passwordInput = (TextInputEditText) findViewById(R.id.passwordInput);
Button registerButton = findViewById(R.id.createAccountButton);

registerButton.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        userNameText = userNameInput.getText().toString();
        emailText = emailInput.getText().toString();
        passwordText = passwordInput.getText().toString();
        if (validateInputs()) {
            saveUser();
        } else {
            Toast.makeText(RegisterActivity.this, "Inputs invalid! Please correct.", Toast.LENGTH_SHORT).show();
        }
    }

    private void saveUser() {
        if (new UserDatabaseModel(userNameText, passwordText, emailText).saveUser()) {
            Toast.makeText(RegisterActivity.this, "User Created!", Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(RegisterActivity.this, "User Creation Failed!", Toast.LENGTH_SHORT).show();
        }
    }

Here is my UserDatabaseModel.java file:

public class UserDatabaseModel {
    public final static String DATABASE_DECLARATION = "user";
    private final FirebaseFirestore db = FirebaseFirestore.getInstance();
    private String username;
    private String password;
    private String email;

    public UserDatabaseModel(String username, String password, String email) {
        this.username = username;
        this.password = password;
        this.email = email;
    }
    public UserDatabaseModel() {

    }

    public boolean saveUser() {

        try {
            db.collection(UserDatabaseModel.DATABASE_DECLARATION).document().set(this);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public String getUsername() {
        return username;
    }
    public String getPassword() {
        return password;
    }
    public String getEmail() {
        return email;
    }
}

My validateInputs()-method does some basic stuff such as checking the length of the inputs, as well as it calls if (userNameExistsAlready()) { return false}.
Now the important part, the userNameExistsAlready()-method:

    private boolean userNameExistsAlready() {
        Query mQuery = db.collection(UserDatabaseModel.DATABASE_DECLARATION)
                .whereEqualTo("username", userNameText);

        mQuery.addSnapshotListener(new EventListener<QuerySnapshot>(){
            @Override
            public void onEvent(QuerySnapshot queryDocumentSnapshots, FirebaseFirestoreException e) {
                for (DocumentSnapshot ds: queryDocumentSnapshots){
                    if (ds!=null && ds.exists()){
                        Toast.makeText(RegisterActivity.this, "Username Exists Already!", Toast.LENGTH_SHORT).show();
                    }
                }
            }
        });
        return false;
}

This code always finds at least one DocumentSnapshot with the given username, the one I just typed in(of course without having it actually saved, because the save-method only gets called if validateInputs() == true[this is the thing that confuses me]), and more if there are more saved already. Do I miss something here (I thought at least the ds.exists()-call would make sure these files are IN the DB already) or what could be the problem?

Thanks

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
tc324
  • 3
  • 1
  • 2

2 Answers2

1

I use it like this

public void checkFieldIsExist(String key, String value, OnSuccessListener<Boolean> onSuccessListener) {
    db.collection("users").whereEqualTo(key, value).addSnapshotListener(new EventListener<QuerySnapshot>() {
        private boolean isRunOneTime = false;

        @Override
        public void onEvent(QuerySnapshot queryDocumentSnapshots, FirebaseFirestoreException e) {
            if (!isRunOneTime) {
                isRunOneTime = true;
                List<DocumentSnapshot> snapshotList = queryDocumentSnapshots.getDocuments();
                if (e != null) {
                    e.printStackTrace();
                    String message = e.getMessage();
                    onSuccessListener.onSuccess(false);
                    return;
                }

                if (snapshotList.size() > 0) {
                    //Field is Exist
                    onSuccessListener.onSuccess(false);
                } else {
                    onSuccessListener.onSuccess(true);
                }

            }
        }
    });
}

Usage

 checkFieldIsExist("username", "dummy", new OnSuccessListener<Boolean>() {
                            @Override
                            public void onSuccess(Boolean aBoolean) {
                                if(aBoolean){
                                    //
                                }else{
                                    //username is exist
                                }
                            }
                        });

You can use it instead of OnSuccesListener

public interface OnCompleteListener<T> {
    void onComplete(T result);
}
  • Hi, thanks for the answer! I modified this code a bit and it reads way better than my code before that(added `String collection` to the `checkFieldIsExist`-method and had to declare the onSuccessListener as final in the method), but it still does not seem to work. Even if the Database is fully cleared, `snapshotList.size()` always reports AT LEAST 1. Any other idea what I could be missing? – tc324 Apr 09 '18 at 16:42
  • Can you debug for see what item came from the list. – Hamza Burakhan Apr 09 '18 at 17:11
  • Sure. All done on a (again) fresh DB. I/System.out: 1 {password=test, email=test@test.com, username=testuser} This is exactly what I entered. I don't understand how this can be queried if I didn't even call the save function yet. edit: this was a `System.out.println(snapshot.getData())` – tc324 Apr 09 '18 at 17:19
  • ctrl+shift+f and search test@test.com in project – Hamza Burakhan Apr 09 '18 at 17:31
  • There indeed was one entry, but this happens with literally any email, username (i even checked for username only atm) or/and password. It also does not appear to be a caching issue in that sense, because I tested this many times with completely different/weird etc. usernames. – tc324 Apr 09 '18 at 17:41
  • Sorry, I'm not sure I understand the problem. `private boolean isRunOneTime = false; @Override public void onEvent(QuerySnapshot queryDocumentSnapshots, FirebaseFirestoreException e) { if (!isRunOneTime) {` Do you use the variable isRunOneTime. Beacuse, the listener is re-triggered when the record is added to the database when it is not used – Hamza Burakhan Apr 09 '18 at 18:09
  • Yes, I use it because I just copied most of your code. My problem is that chedkFieldIsExist always finds something, even though I (think) I haven't called the save() method yet. Say in my activity, I enter ANYthing that is valid (valid email, etc.), it will always say that it found an entry with this Username (but how? I haven't saved it yet I think) and thus won't save. – tc324 Apr 09 '18 at 19:20
0

First of all, there is no need to use addSnapshotListener. This is used only when you want to get realtime updates from the database but it's not your case. You only want to check a username for existens and for that, only a get() call is required.

mQuery.get().addOnCompleteListener(/* ... */)

Second, according to this post, ds!=null && ds.exists() is not required. You only need to check if ds.exists().

And the last thing you need to know is that you can not rely on what value your userNameExistsAlready() method returns. In most cases it will be false and this is because the onEvent() method in your case or onComplete() / onSuccess() in case of using a get() call, are asynchronous. This means that by the time your method returns the result, you haven't finished loading the data from the database. So a quick solve for this would be to use all your logic inside the onEvent() method or if you want to use it outside, I recommend you see the last part of my anwser from this post and also take a look at this video.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Hi, thanks! I've now only added a onCompleteListener in which I basically call `task.getResult().getDocuments();` and afterwords go through that list to filter out all nonexisting ones: `for (DocumentSnapshot snapshot : snapshots) { if (!snapshot.exists()) { snapshots.remove(snapshot)}}` and afterwords check the size of the list and yes: It really works now :D Now I just need to figure out an elegant way to check all relevant fields beforehand, and not just the `username`. Thanks for the help, especially with the insight you provided(the queries being asynchronous etc.) – tc324 Apr 11 '18 at 17:26