0

PROBLEM:

I am getting MutableData value as null until I click the onClick multiple times (which is infeasible for a user to do).

I am trying to increment a value that is in the database for number of employees whenever an employee is added. I researched into Transactions and saw the following (Firebase runTransaction not working) and (https://www.firebase.com/docs/android/guide/saving-data.html#section-transactions) and followed this documentation.

Steps to recreate problem using debugger:

  1. When it enters the incrementNumEmp and goes to the doTransaction method I get this at the if (note that starting value in database is 3 before any clicks):

MutableData { key = , value = null }"

  1. I click the checkmark button (onClick)
  2. I click Resume Program in the debugger. It goes from Transaction.success back to the if statement and now it has:

"MutableData { key = , value = 4}"

DB Structure:

enter image description here

Relevant Code:

private void incrementNumEmp(String compid) {

        Log.i("insideIncrementMethod", compid);
        mCompTotalEmpRef.child(compid).child("CompanyTime").child("TotalEmployees").runTransaction(new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {
                if(mutableData.getValue() !=null) {
                    mutableData.setValue((Long) mutableData.getValue() + 1);
                }
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean b,
                                   DataSnapshot dataSnapshot) {
                Log.d("TAG", "countTransaction:onComplete:" + databaseError);
            }
        });
    }

Entirety of code:

public class RegisterEmployeeActivity extends Activity {

    EditText firstName, lastName, phoneNumber, emailET, ssnET, passwordET;
    String email, password, ssn, fName, lName, phone;
    String companyID;
    boolean fieldsFilled;

    private ImageView checkmarkImage;
    private FirebaseAuth auth;
    DatabaseReference mDatabase;
    FirebaseStorage storage;
    StorageReference storageReference;

    //PHOTO STUFF
    StorageMetadata metadata;
    UploadTask uploadTask;
    Uri file;


    private static FirebaseUser currentUser;
    String currentUserString;
    FirebaseDatabase database;
    DatabaseReference mDatabaseEmp, mDatabaseComp, mDatabaseUnverified;
    DatabaseReference mDatabseCompEmp;
    DatabaseReference mCompTotalEmpRef;

    //PHONE STUFF
    PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks;
    String mVerificationId;
    PhoneAuthProvider.ForceResendingToken mResendToken;
    FirebaseAuth mAuth;


    private FirebaseAuth.AuthStateListener mAuthListener;

    private FirebaseMethods firebaseMethods;
    private Context mContext;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register_employee);

        firstName = (EditText) findViewById(R.id.firstNameET);
        lastName = (EditText) findViewById(R.id.lastNameET);
        phoneNumber = (EditText) findViewById(R.id.phoneET);
        emailET = (EditText) findViewById(R.id.emailET);
        ssnET = (EditText) findViewById(R.id.ssnET);
        passwordET = (EditText) findViewById(R.id.passwordET);



        //Get Firebase auth instance
        auth = FirebaseAuth.getInstance();

        //PHOTO STORAGE
        storage = FirebaseStorage.getInstance();
        storageReference = storage.getReferenceFromUrl("gs://timeclock-fc.appspot.com");
        //storageReference = storage.getReferenceFromUrl("gs://timeclock-fc.appspot.com").child("20170702_174811.jpeg"); //was PNG

        //FIREBASE METHODS
        mContext = RegisterEmployeeActivity.this;
        firebaseMethods = new FirebaseMethods(mContext);

        //DATABASE REFERENCES
        mDatabase = FirebaseDatabase.getInstance().getReference("/Unverified Employees");
        //Log.i("db ref", mDatabase.child("RC9zIioE6vc5vlhrIethmbqyFDS2").getKey());
        database = FirebaseDatabase.getInstance();
        mDatabaseEmp = database.getReference("/Employees");
        mDatabaseComp = database.getReference("/Companies");
        mDatabaseUnverified = database.getReference("/Unverified Employees");
        mDatabseCompEmp = database.getReference("/CompanyEmployees/CompanyIDs");
        mCompTotalEmpRef = database.getReference("/CompanyEmployeeTime/CompanyIDs");

        currentUser =
                FirebaseAuth.getInstance().getCurrentUser();


         mAuth = FirebaseAuth.getInstance();


        //final ImageView checkmarkImage = (ImageView) findViewById(R.id.checkmarkImage);
        checkmarkImage = (ImageView) findViewById(R.id.checkmarkImage);

        //Insert information into FirebaseDB and go to next screen
        checkmarkImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(final View view) {
            //public void onClick(View view) {

                //Assigns EditText values to String values
                assignStringValues();


                //Ensures that EditText fields are filled. If not will give a warning
                fieldsFilled = areFieldsFilled();

                mDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
                    @Override
                    public void onDataChange(DataSnapshot dataSnapshot) {

                        Log.i("DATA VALUE", dataSnapshot.child("RC9zIioE6vc5vlhrIethmbqyFDS2").getValue().toString());

                        //Used https://github.com/mitchtabian/Android-Instagram-Clone/tree/6e2ffe29621c592e57057b8561d3cac4df9c14a1/app/src/main/java/tabian/com/instagramclone  and https://www.youtube.com/watch?v=I-2T4i75gfw


                        Log.i("EMAIL NULL?", email);
                        Log.i("SSN", ssn);
                        Log.i("ds/Input", dataSnapshot.toString());
                        companyID = firebaseMethods.checkIfSsnExists(ssn, dataSnapshot);
                        //Log.i("companyIDRegister", companyID);
                        if(email!= null && !email.equals("")) {
                            //CHECK IF IT MATCHES (SSN) *AND* Email
                            //companyID = firebaseMethods.checkIfSsnExists(ssn, dataSnapshot);
                            if (firebaseMethods.checkIfEmailExists(email, dataSnapshot) && companyID.length()>0) {

                                //if (firebaseMethods.checkIfEmailExists(email, dataSnapshot) && firebaseMethods.checkIfSsnExists(ssn, dataSnapshot)) {
                                auth.createUserWithEmailAndPassword(emailET.getText().toString(), passwordET.getText().toString())
                                        .addOnCompleteListener(RegisterEmployeeActivity.this, new OnCompleteListener() {
                                            @Override
                                            public void onComplete(@NonNull Task task) {
                                                //progressBar.setVisibility(View.GONE);
                                                if (!task.isSuccessful()) {
                                                    //EXCEPTION MESSAGES
                                                    try {
                                                        throw task.getException();
                                                    } catch (FirebaseAuthWeakPasswordException e) {
                                                        MethodHelper.showAlert(RegisterEmployeeActivity.this, "Authentication failed.", "Password should be atleast 6 characters");
                                                    } catch (FirebaseAuthInvalidCredentialsException e) {
                                                        MethodHelper.showAlert(RegisterEmployeeActivity.this, "Authentication failed.", "The email address is badly formatted");
                                                    } catch (FirebaseAuthUserCollisionException e) {
                                                        MethodHelper.showAlert(RegisterEmployeeActivity.this, "Authentication failed.", "The email address is already in use by another account");

                                                    } catch (Exception e) {
                                                        e.printStackTrace();
                                                    }

                                                } else {

                                                    //SEND INFORMATION TO FIREBASE DATABASE
                                                    //companyID = firebaseMethods.getCompanyID();
                                                    //addEmployeeToFirebase(currentUser);
                                                    currentUser =
                                                            FirebaseAuth.getInstance().getCurrentUser();
                                                    Log.i("beforeAddEmail",currentUser.getUid());
                                                    addEmployeeToFirebase(currentUser, companyID);
                                                    removeEmail();


                                                    /*if(fieldsFilled == true) {
                                                        startActivity(new Intent(RegisterEmployeeActivity.this, EmployeeHome.class));
                                                        finish();
                                                    } */

                                                }
                                            } //END OF ONCOMPLETE
                                        }); //END OF ONCOMPLETELISTENER
                            } //END OF (IF) CHECKEMAILEXISTS
                            else {
                                MethodHelper.showAlert(RegisterEmployeeActivity.this, "Authentication failed.", "You must use the email and ssn your employer has entered");
                            }
                        }  //END OF EMAIL CHECK

                        //PHONE # CHECK
                        if(phone != null && !phone.equals("")) {
                            //CHECK IF SSN *AND* PHONE EXISTS
                            //companyID.length()>0
                            if (firebaseMethods.checkIfPhoneExists(phone, dataSnapshot) && companyID.length()>0) {

                                //if (firebaseMethods.checkIfPhoneExists(phone, dataSnapshot) && firebaseMethods.checkIfSsnExists(ssn, dataSnapshot)) {


                                Log.i("IF", "INSIDE IF");
                                mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

                                    //USING https://github.com/firebase/quickstart-android/blob/master/auth/app/src/main/java/com/google/firebase/quickstart/auth/PhoneAuthActivity.java
// Originally https://firebase.google.com/docs/auth/android/phone-auth
                                    @Override
                                    public void onVerificationCompleted(PhoneAuthCredential credential) {
                                        // This callback will be invoked in two situations:
                                        // 1 - Instant verification. In some cases the phone number can be instantly
                                        //     verified without needing to send or enter a verification code.
                                        // 2 - Auto-retrieval. On some devices Google Play services can automatically
                                        //     detect the incoming verification SMS and perform verification without
                                        //     user action.
                                        Log.i("VERIFICATION_COMPLETED", "onVerificationCompleted:" + credential);

                                        //unnecessary?
                                        // [START_EXCLUDE silent]
                                        //mVerificationInProgress = false;
                                        // [END_EXCLUDE]

                                        // [START_EXCLUDE silent]
                                        // Update the UI and attempt sign in with the phone credential
                                        //updateUI(STATE_VERIFY_SUCCESS, credential);
                                        // [END_EXCLUDE]
                                        //TODO: See why this is no longer called
                                        signInWithPhoneAuthCredential(credential);
                                    }


                                    @Override
                                    public void onVerificationFailed(FirebaseException e) {
                                        // This callback is invoked in an invalid request for verification is made,
                                        // for instance if the the phone number format is not valid.
                                        Log.i("VERIFICATION_FAILED", "onVerificationFailed", e);

                                        if (e instanceof FirebaseAuthInvalidCredentialsException) {
                                            // Invalid request
                                        } else if (e instanceof FirebaseTooManyRequestsException) {
                                            // The SMS quota for the project has been exceeded
                                        }

                                        // Show a message and update the UI
                                    }

                                    //Looks Good
                                    @Override
                                    public void onCodeSent(String verificationId,
                                                           PhoneAuthProvider.ForceResendingToken token) {
                                        // The SMS verification code has been sent to the provided phone number, we
                                        // now need to ask the user to enter the code and then construct a credential
                                        // by combining the code with a verification ID.
                                        Log.i("CODE_SENT", "onCodeSent:" + verificationId);

                                        // Save verification ID and resending token so we can use them later
                                        mVerificationId = verificationId;
                                        mResendToken = token;
                                        //PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, mResendToken);

                                        // ...
                                    }

                                }; //END OF MCALLBACKS



                            verifyPhone(phone, mCallbacks);


                                    } //END OF (IF) CHECK IF PHONE EXISTS
                            else {
                                MethodHelper.showAlert(RegisterEmployeeActivity.this, "Authentication failed.", "You must use the phone number and ssn your employer has entered");

                            }
                        }  //END OF PHONE# Check
                    } //END OF ONDATACHANGE

                    @Override
                    public void onCancelled(DatabaseError databaseError) {
                    } //CLOSE ONCANCELLED

                }); //END OF mDatabase Listener

                // } //END OF ONAUTHSTATECHANGED
                // }; //END OF FIREBASE AUTH STATE LISTENER
            } //END OF ONCLICK
        }); //END OF CHECKMARK


    }  //END OF ONCREATE

    private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
        //Log.i("mAuth", mAuth.toString());
        //FirebaseAuth.getInstance().signInWithCredential(credential)
        mAuth.signInWithCredential(credential)
                .addOnCompleteListener(RegisterEmployeeActivity.this, new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if (task.isSuccessful()) {
                            // Sign in success, update UI with the signed-in user's information
                            Log.i("SIGNINWITHCREDENTIAL", "signInWithCredential:success");

                            FirebaseUser user = task.getResult().getUser();
                            // ...
                        }

                        else {
                            // Sign in failed, display a message and update the UI
                            Log.i("SIGNINWITHCREDENTIAL", "signInWithCredential:failure", task.getException());
                            if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                                // The verification code entered was invalid
                            }
                        }

                        assignStringValues();

                        currentUser =
                                FirebaseAuth.getInstance().getCurrentUser();

                        //SEND INFORMATION TO FIREBASE DATABASE
                        companyID = firebaseMethods.getCompanyID();
                        Log.i("companyID", companyID);
                        addEmployeeToFirebase(currentUser, companyID);
                        //     addEmployeeToFirebase(currentUser);


                        Log.i("fieldsFilled", String.valueOf(fieldsFilled));
                        if(fieldsFilled == true) {
                            finish();
                            startActivity(new Intent(RegisterEmployeeActivity.this, EmployeeHome.class));
                            //finish();
                        }

                    } //End of OnComplete
                });
    }

    //PhoneAuthProvider.OnVerificationStateChangedCallbacks()
    public void verifyPhone(String phone, PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks)
    {
        PhoneAuthProvider.getInstance().verifyPhoneNumber(
                phone,        // Phone number to verify
                120,                 // Timeout duration
                TimeUnit.SECONDS,   // Unit of timeout
                RegisterEmployeeActivity.this,               // Activity (for callback binding)
                mCallbacks);        // OnVerificationStateChangedCallbacks
    }

    DatabaseReference.CompletionListener completionListener =
            new DatabaseReference.CompletionListener() {
                @Override
                public void onComplete(DatabaseError databaseError,
                                       DatabaseReference databaseReference) {

                    if (databaseError != null) {
                        notifyUser(databaseError.getMessage());
                    }
                }
            };

    private void notifyUser(String message) {
        Toast.makeText(RegisterEmployeeActivity.this, message,
                Toast.LENGTH_SHORT).show();
    }

    private void assignStringValues() {
        email = emailET.getText().toString().trim();
        password = passwordET.getText().toString().trim();
        ssn = ssnET.getText().toString().trim();
        fName = firstName.getText().toString().trim();
        lName = lastName.getText().toString().trim();
        phone = phoneNumber.getText().toString().trim();
    }

    private boolean areFieldsFilled() {


        fieldsFilled = true;
        if (TextUtils.isEmpty(fName)) {
            MethodHelper.showAlert(RegisterEmployeeActivity.this, "First Name Required", "A First Name is required. Please enter your name and try again.");
            fieldsFilled = false;

        }


        if (TextUtils.isEmpty(lName)) {
            MethodHelper.showAlert(RegisterEmployeeActivity.this, "Last Name Required", "A Last Name is required. Please enter your name and try again.");
            fieldsFilled = false;
        }


        if ( (TextUtils.isEmpty(phone)) && TextUtils.isEmpty(email)) {
            MethodHelper.showAlert(RegisterEmployeeActivity.this, "Phone Number OR Email Required", "A Phone Number OR Email is required. Please enter your number OR email and try again.");
            fieldsFilled = false;
        }


        if (TextUtils.isEmpty(password)) {
            MethodHelper.showAlert(RegisterEmployeeActivity.this, "Password Required", "A password is required. Please enter your password and try again.");
            fieldsFilled = false;
        }


        if(TextUtils.isEmpty(ssn)) {
            MethodHelper.showAlert(RegisterEmployeeActivity.this, "Social Security Number Required", "A social security number is required. Please enter the last 4 digits of your social security number and try again.");
            fieldsFilled = false;
        }
        return fieldsFilled;

    }

    private void addEmployeeToFirebase(FirebaseUser currentUser, final String compid) {
        //SEND INFORMATION TO FIREBASE DATABASE

        // mDatabaseEmp.child(currentUser.getUid()).child("firstName")
        //        .setValue(fName, completionListener);
        //May want to set currentUser.getUid()) to a String?

        Log.i("addEmployeeToFirebase", "inside method");

        currentUserString = currentUser.getUid();
        Log.i("currentUserStringADDEMP", currentUserString);
        mDatabaseEmp.child(currentUserString).child("firstName")
                .setValue(fName, completionListener);
        mDatabaseEmp.child(currentUserString).child("lastName")
                .setValue(lName, completionListener);
        mDatabaseEmp.child(currentUserString).child("phoneNumber")
                .setValue(phone, completionListener);
        mDatabaseEmp.child(currentUserString).child("email")
                .setValue(email, completionListener);
        mDatabaseEmp.child(currentUserString).child("ssn")
                .setValue(ssn, completionListener);
        Log.i("addEmp-CompID?", compid);
        mDatabaseEmp.child(currentUserString).child("CompanyID").setValue(compid, completionListener);
        mDatabseCompEmp.child(compid).child("EmployeeIDs").child(currentUserString).child("ID").setValue(currentUserString);
        //INCREMENT Total # Employees
        /*mCompTotalEmpRef.child(compid).child("CompanyTime").addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                try {

                    long total = Long.parseLong(dataSnapshot.child("TotalEmployees").getValue().toString());
                    total = total + 1;
                    mCompTotalEmpRef.child(compid).child("CompanyTime").child("TotalEmployees").setValue(total);
                }
                catch (Exception e) {  Log.i("LNull?", e.toString());     }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {   Log.d("Cancelled",databaseError.toString());     }
        }); */


        incrementNumEmp(compid);


    }

    private void incrementNumEmp(String compid) {

        Log.i("insideIncrementMethod", compid);
        mCompTotalEmpRef.child(compid).child("CompanyTime").child("TotalEmployees").runTransaction(new Transaction.Handler() {
            @Override
            public Transaction.Result doTransaction(MutableData mutableData) {
                //long total = Long.parseLong(mutableData.getValue().toString()); //why is mutable data key none value null??
               /* long total = 0;
                if(mutableData.getValue(Long.class) != null) {
                    String numEmp = (String) mutableData.getValue();
                    //total = Long.parseLong(numEmp);
                    total = Long.parseLong(numEmp, 16);

                } */

                if(mutableData.getValue() !=null) {
                    mutableData.setValue((Long) mutableData.getValue() + 1);
                }
                //total = mutableData.getValue(Long.class);

               /* Log.i("totalBefore", String.valueOf(total));
                total++;
                Log.i("totalAfter", String.valueOf(total));
                String incHex = Long.toHexString(total); */

                //mutableData.setValue(total);
                //mutableData.setValue(incHex);
                return Transaction.success(mutableData);
            }

            @Override
            public void onComplete(DatabaseError databaseError, boolean b,
                                   DataSnapshot dataSnapshot) {
                Log.d("TAG", "countTransaction:onComplete:" + databaseError);
            }
        });
    }

    private void removeEmail() {
        /*Query emailQuery = mDatabase.child("emailAddress").equalTo(email);
        Log.i("emailRemove", email);
        Log.i("removeEmail","Inside");
        //mDatabaseEmp.child("emailAddress").equalTo(email).setValue("");

        //mDatabaseUnverified.child("emailAddress").equalTo(email).removeEventListener();
        emailQuery.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for (DataSnapshot emailSnapshot: dataSnapshot.getChildren()) {
                    Log.i("snapshot", emailSnapshot.toString());
                    Log.i("snapshotRef", emailSnapshot.getRef().toString());
                    emailSnapshot.getRef().removeValue();

                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.e("TAG", "onCancelled", databaseError.toException());
            }
        }); */


        /*Query queryRef = mDatabase.orderByChild("Unverified Employees").equalTo(email);

        queryRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot snapshot, String previousChild) {
                Log.i("snapshot", snapshot.toString());
                Log.i("snapshotRef", snapshot.getRef().toString());
                snapshot.getRef().setValue(null);
            }

            @Override
            public void onChildRemoved(DataSnapshot snapshot) {
                Log.e("TAG","onChildRemoved "+ snapshot.toString());
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {

            }

            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                Log.e("TAG","onChildChanged "+ dataSnapshot.toString()+" \n String "+s);
            }

            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {
                Log.e("TAG","onChildAdded "+ dataSnapshot.toString()+" \n String "+s);
            }

        }); */

        Query emailQuery = mDatabase.child("emailAddress").equalTo(email);

        emailQuery.addListenerForSingleValueEvent(new ValueEventListener() {
            @Override
            public void onDataChange(DataSnapshot dataSnapshot) {
                for (DataSnapshot emailSnapshot: dataSnapshot.getChildren()) {
                    Log.i("snapshot", emailSnapshot.toString());
                    Log.i("snapshotRef", emailSnapshot.getRef().toString());
                    emailSnapshot.getRef().removeValue();
                }
            }

            @Override
            public void onCancelled(DatabaseError databaseError) {
                Log.e("TAG", "onCancelled", databaseError.toException());
            }
        });
    }



}
Peter Haddad
  • 78,874
  • 25
  • 140
  • 134
ImTheNun
  • 167
  • 1
  • 10
  • Possible duplicate of [How to save users score in firebase and retrieve it in real-time in Android studio](https://stackoverflow.com/questions/48307610/how-to-save-users-score-in-firebase-and-retrieve-it-in-real-time-in-android-stud) – Alex Mamo Mar 13 '18 at 18:36
  • Please take a look at the duplicate to see the steps that you need to follow in order to write data using transactions. – Alex Mamo Mar 13 '18 at 18:37
  • I had followed that article and was not getting the right answer. The original poster does not show their entire code so I could not see where my particular problem was. This is why we always say to post the full code, and I understand the importance. – ImTheNun Mar 13 '18 at 19:52

1 Answers1

0

So after putting many more log statements, it seems that my "finish()" was in the wrong place. Since the DB Listener is asynchronous, finish must be called before the Transactions mutable it seems.


Inside of the DB Listener I originally had:

addEmployeeToFirebase(currentUser, companyID);
finish();

I changed it to the following and now it works!:

finish();
addEmployeeToFirebase(currentUser, companyID);

What's odd to me is the other .setValues work just fine, but for the Transaction mutableData I had to have finish before it

ImTheNun
  • 167
  • 1
  • 10