I want to give the user in my app the possibility to delete his account so when he clicks on the delete button a document gets deleted which contains all his informations. The name of the document is his displayName so I get this as a string but when I run the code you are seeing below I get a NullpointerException in this line:
String currentUsername = user.getDisplayName();
even though the displayName is not null.
Edit:
I found the solution on my own, see the answer below.
Here is my method:
btn_delete_account.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
user.delete()
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
deleteDocument();
}
}
});
}
});
...
public void deleteDocument (){
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String currentUsername = user.getDisplayName();
db.collection("User").document(currentUsername)
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully deleted!");
Toast.makeText(PersonalSettings.this, "Your account was successfully deleted.", Toast.LENGTH_SHORT).show();
Intent i = new Intent(PersonalSettings.this, SignInActivity.class);
startActivity(i);
finish();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error deleting document", e);
}
});
}
First thing you have to check that current user is not null
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if(user==null)
{
return;
}
if current user is not null then get its name and further check that it's name is not null.
String currentUsername = user.getDisplayName();
if(TextUtils.isEmpty(currentUsername))
{
return;
}
if name is not null then go for delete document as follows :
public void deleteDocument (){
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if(user==null)
{
return;
}
String currentUsername = user.getDisplayName();
if(TextUtils.isEmpty(currentUsername))
{
return;
}
db.collection("User").document(currentUsername)
.delete()
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "DocumentSnapshot successfully deleted!");
Toast.makeText(PersonalSettings.this, "Dein Account wurde erfolgreich gelöscht.", Toast.LENGTH_SHORT).show();
Intent i = new Intent(PersonalSettings.this, SignInActivity.class);
startActivity(i);
finish();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error deleting document", e);
}
});
}
I think you're misunderstanding the error. It's saying that user is null, not the display name. This means there is currently no user signed into the app. You will have to write some code to check for this case.
I also strongly suggest not using a display name as the ID for a document in Cloud Firestore. Since you're using Firebase Authentication, the user already has a unique ID assigned to their account. This is the preferred way to store per-user data.
I found the error:
I called my delete method after I used the user.delete() method which deletes the signed in user, so logically the displayName was also deleted.
Related
I'm trying to make a database of users with profile pictures(stored in storage) related to their IDs.
I'm using Firebase auth,Realtime database and Realime storage.
The code work like this : In user registration process, they choose username and picture. The database is filled with Users -> userUUid -> username and URL of picture stored.
The storage stores the picture.
If I keep such name of the table (Users) code works fine, however if I try to manipulate the tables, for example if I want to make more categories like Users -> SpecialUsers / NormalUsers -> userUUid -> ... the app just crashes.. I tried debugging but saw no errors.
Is there any way how to catch these Firebase exceptions (if there is any) or any explanation why if I change the structure of the tables, the code doesn't work ?
These is the method responsible for the registration :
(this one works)
public void signUp(String email,String password,String userName)
{
auth.createUserWithEmailAndPassword(email,password).addOnCompleteListener(task -> {
if (task.isSuccessful())
{
dbReference.child("Users").child(auth.getUid()).child("userName").setValue(userName);
//if user choose some picture
if(imageControl)
{
UUID randomID = UUID.randomUUID(); //create random UUID for the picture
final String imageName = "images/"+randomID+".jpg";
storageReference.child(imageName).putFile(imageUri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
StorageReference myStorageRef = firebaseStorage.getReference(imageName);
myStorageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
String filePath = uri.toString();
dbReference.child("Users").child(auth.getUid()).child("image").setValue(filePath).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(RegistrationActivity.this, "Write to database is successful.", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(RegistrationActivity.this, "Write to database is not successful.", Toast.LENGTH_SHORT).show();
}
});
}
});
}
});
}
else
{
dbReference.child("Users").child(auth.getUid()).child("image").setValue("null");
}
Intent intent = new Intent(RegistrationActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
else
{
Toast.makeText(RegistrationActivity.this, "There is a problem.", Toast.LENGTH_SHORT).show();
}
});
}
This doesn't work :
(As you can see the only thing changed is the structure of the tables from only 'Users' to 'superUsers' -> 'anotherCategory')
public void signUp(String email,String password,String userName)
{
auth.createUserWithEmailAndPassword(email,password).addOnCompleteListener(task -> {
if (task.isSuccessful())
{
dbReference.child("superUsers").child('anotherCategory').child(auth.getUid()).child("userName").setValue(userName);
//if user choose some picture
if(imageControl)
{
UUID randomID = UUID.randomUUID(); //create random UUID for the picture
final String imageName = "images/"+randomID+".jpg";
storageReference.child(imageName).putFile(imageUri).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() {
#Override
public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) {
StorageReference myStorageRef = firebaseStorage.getReference(imageName);
myStorageRef.getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
String filePath = uri.toString();
dbReference.("superUsers").child('anotherCategory').child(auth.getUid()).child("image").setValue(filePath).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(RegistrationActivity.this, "Write to database is successful.", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(RegistrationActivity.this, "Write to database is not successful.", Toast.LENGTH_SHORT).show();
}
});
}
});
}
});
}
else
{
dbReference.("superUsers").child('anotherCategory').child(auth.getUid()).child("image").setValue("null");
}
Intent intent = new Intent(RegistrationActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
else
{
Toast.makeText(RegistrationActivity.this, "There is a problem.", Toast.LENGTH_SHORT).show();
}
});
}
I suspect that the errors occurs somewhere after imageControl check.
In the second example of the code, the tables superUsers -> anotherCategory tables are created in the database, however the reference to the picture UUids is empty (not even null is placed)
What could be the error if nothing relevant to the process of storing pictures is changed ?
Want to update profile, started with email to get the hang off. Whenever I click on it to update the email, it adds a new UID for that email alone instead of adding it to the user profile with that UID. Really stuck now. I did have it working but cant seem to get back to it so must be an error here somewhere, thanks,
updateProfile.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String updateEmail = emailTextView.getText().toString();
user.updateEmail(updateEmail).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
firebaseDatabase.getReference("Users").child(user.getUid()).child("email").setValue(updateEmail);
Toast.makeText(getApplicationContext(), "Email update", Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
});
}
UPDATE
firebaseAuth = FirebaseAuth.getInstance();
firebaseDatabase = FirebaseDatabase.getInstance();
FirebaseUser firebaseUser = firebaseAuth.getCurrentUser();
final String uid = firebaseUser.getUid();
Log.d(TAG, "USERID="+uid);
Doin this got me a UID from a completely different user! It did let me change the email for them when I did this but thats no use. Having similar issues deleting users. Is it an issue with a UID does it create a new one everytime they log in?
i tried to do everything correctly and can't seem to find what's wrong here, i even created the project again from scratch but still it doesn't work, but i get the "failed" toast when trying to create an account, i added the internet permission too. i also don't get any error in logcat to show it here, how can this be solved ?
public class CreateAccountActivity extends AppCompatActivity {
private Button btnCreateAcc;
private FirebaseAuth firebaseAuth;
private FirebaseAuth.AuthStateListener authStateListener;
private FirebaseUser currentUser;
// firestore
private FirebaseFirestore database = FirebaseFirestore.getInstance();
private CollectionReference collectionReference = database.collection("Users");
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_create_account);
firebaseAuth = FirebaseAuth.getInstance();
authStateListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
currentUser = firebaseAuth.getCurrentUser();
if (currentUser != null) {
} else {
}
}
};
btnCreateAcc = findViewById(R.id.create_acct_button);
btnCreateAcc.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (!TextUtils.isEmpty(etEmail.getText().toString()) &&
!TextUtils.isEmpty(etPassword.getText().toString()) &&
!TextUtils.isEmpty(etUserName.getText().toString())) {
String email = etEmail.getText().toString();
String password = etPassword.getText().toString();
String username = etUserName.getText().toString();
createUserEmailAccount(email, password, username);
} else {
Toast.makeText(CreateAccountActivity.this, "Please fill in all fields"
, Toast.LENGTH_SHORT).show();
}
}
});
}
private void createUserEmailAccount(String email, String password, final String username) {
if (!TextUtils.isEmpty(email) && !TextUtils.isEmpty(password) &&
!TextUtils.isEmpty(username)) {
progressBar.setVisibility(View.VISIBLE);
firebaseAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
currentUser = firebaseAuth.getCurrentUser();
assert currentUser != null;
final String currentUserId = currentUser.getUid();
Map<String, String> userObj = new HashMap<>();
userObj.put("userId", currentUserId);
userObj.put("username", username);
collectionReference.add(userObj)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
documentReference.get()
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (Objects.requireNonNull(task.getResult()).exists()) {
progressBar.setVisibility(View.INVISIBLE);
String name = task.getResult()
.getString("username");
Intent intent = new Intent(CreateAccountActivity.this,
PostJournalActivity.class);
intent.putExtra("username", name);
intent.putExtra("userId", currentUserId);
startActivity(intent);
} else {
progressBar.setVisibility(View.INVISIBLE);
}
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(CreateAccountActivity.this, "failed"
, Toast.LENGTH_SHORT).show();
}
});
} else {
Toast.makeText(CreateAccountActivity.this, "failed task"
, Toast.LENGTH_SHORT).show();
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(CreateAccountActivity.this, "failed on complete"
, Toast.LENGTH_SHORT).show();
}
});
} else {
Toast.makeText(this, "failed else", Toast.LENGTH_SHORT).show();
}
}
#Override
protected void onStart() {
super.onStart();
currentUser = firebaseAuth.getCurrentUser();
firebaseAuth.addAuthStateListener(authStateListener);
}
}
i fixed, the problem was i had allow read, write: if false; in firestore
and it needed to be allow read, write: if true;
This could be solved by following good debugging practices.
Meaningful error messages
When you encounter an error and wish to send a toast/alert to the user, send something meaningful to inform them what went wrong. As an example, instead of "failed" or "failed task", use "failed to upload user data" or "failed to create new user".
Each function will normally return a handful of exception classes that can be used to provide a better toast/alert message. Consulting the documentation for createUserWithEmailAndPassword(email, password) you can see what exceptions are thrown and use instanceof to determine the cause of the problem. For example, if e instanceof FirebaseAuthInvalidCredentialsException was true, you could toast "failed to create new user: invalid email".
While this seems tedious, it will save head-scratching later when a user encounters a problem and sends you a bug report/email about it. These steps will help you find any issues without needing access to logs for trivial problems such as incorrectly filled forms.
Log exceptions
The reason you have no information on what went wrong is because you haven't made use of the exception provided in each onFailure handler (public void onFailure(#NonNull Exception e) { ... }). These handlers provide you with the exception that caused the problem which you can save to the log using Log.e("yourActivityName:yourFunctionName", "short message", e). You can also use e.getMessage() to get information about the thrown error.
In an onComplete(Task<?> task) handler, if task.isSuccessful() returns false, you can find out why it is false by calling Exception e = task.getException() and then log it.
Fail-fast programming
If you ever find that you have an if-else pair where the if section contains lots more code than the else section, it is likely to be a sign that you should flip the condition.
Whilst keeping your code cleaner by using less indentation, it also avoids having to scroll through a long if that probably contains more if and else statements.
For example,
if (!requiredVariable1.isEmpty() && !requiredVariable2.isEmpty()) {
// ...
// many (nested) lines of code
// ...
} else {
Log.e(TAG, "a required variable was empty");
}
if (requiredVariable1.isEmpty() || requiredVariable2.isEmpty()) {
Log.e(TAG, "a required variable was empty");
return;
}
// ...
// many lines of code
// ...
Example
As an example of applying these changes, I have made edits to the code you provided applying fail-fast techniques, simplifying error handling, logging exceptions, using OnSuccessListener and OnFailureListener instead of OnCompleteListener where appropriate,
private void createUserEmailAccount(String email, String password, final String username) {
if (TextUtils.isEmpty(email) || TextUtils.isEmpty(password) || TextUtils.isEmpty(username)) {
Toast.makeText(this, "Please fill in all fields", Toast.LENGTH_SHORT).show();
return;
}
progressBar.setVisibility(View.VISIBLE);
firebaseAuth.createUserWithEmailAndPassword(email, password)
.addOnSuccessListener(new OnSuccessListener<AuthResult>() {
#Override
public void onSuccess(#NonNull Task<AuthResult> task) {
currentUser = firebaseAuth.getCurrentUser();
assert currentUser != null;
final String currentUserId = currentUser.getUid();
Map<String, String> userObj = new HashMap<>();
userObj.put("userId", currentUserId);
userObj.put("username", username); // unknown source for variable: username
collectionReference.add(userObj)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
documentReference.get() // why redownload from database? you could just use values of "userObj"
.addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
progressBar.setVisibility(View.INVISIBLE);
if (!task.isSuccessful()
|| !Objects.requireNonNull(task.getResult()).exists()) {
// show a error message?
return;
}
String name = task.getResult()
.getString("username");
Intent intent = new Intent(CreateAccountActivity.this,
PostJournalActivity.class);
intent.putExtra("username", name);
intent.putExtra("userId", currentUserId);
startActivity(intent);
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(CreateAccountActivity.this, "failed to add user data"
, Toast.LENGTH_SHORT).show();
Log.e("CreateAccountActivity", "failed to add user data", e); // log error to logcat
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(CreateAccountActivity.this, "failed to create user"
, Toast.LENGTH_SHORT).show();
Log.e("CreateAccountActivity", "failed to create user", e); // log error to logcat
}
});
}
I am making an android application using Firebase database and I want to check that the user is not in registered as an "Association" so I am checking if he belongs to the child "Association".
The method userLogin is supposed to not log in the user if he is under the child "Associations" and log in him otherwise.
However, it is not working and the user is logged in even if he is under "Associations"
private void userLogin() {
String email = editTextEmail.getText().toString().trim();
String password = editTextPassword.getText().toString().trim();
mAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
progressBar.setVisibility(View.GONE);
if (task.isSuccessful()) {
FirebaseUser currentUser = FirebaseAuth.getInstance().getCurrentUser();
String RegisteredUserID = currentUser.getUid();
DatabaseReference jLoginDatabase = FirebaseDatabase.getInstance().getReference().child("Associations").child(RegisteredUserID);
jLoginDatabase.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
Toast.makeText(getApplicationContext(), "You are not registered", Toast.LENGTH_SHORT).show();
}
else
{
finish();
Intent intent = new Intent(SignInDonor.this, homedonor.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}});}
else {
Toast.makeText(getApplicationContext(), task.getException().getMessage(), Toast.LENGTH_SHORT).show();
}
}
});
}
I did not tried this, but it should work, first, after you log in with a user and enters inside task.isSuccessful , you can retrieve the current logged in user with task.getResult().getUser().getUid(). Then just loop inside Associations and get each user key (I assume that Associations has userIDs inside as nodes with a certain value), then compare if the current logged in user is equal to one inside that node, if matchs it will pop up your Toast, if not you will be redirected.
Try this
public void onComplete(#NonNull Task<AuthResult> task) {
progressBar.setVisibility(View.GONE);
if (task.isSuccessful()) {
DatabaseReference jLoginDatabase = FirebaseDatabase.getInstance().getReference().child("Associations");
jLoginDatabase.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for(DataSnapshot snapshot : dataSnapshot.getChildren()){
if(snapshot.getKey().equals(task.getResult().getUser().getUid()) {
Toast.makeText(getApplicationContext(), "You are not registered", Toast.LENGTH_SHORT).show();
}
else
{
finish();
Intent intent = new Intent(SignInDonor.this, homedonor.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
}
I used addListenerForSingleValueEvent because we only need to loop once at the reference and not keep listening for data
I'm trying to create an profile activity, where users can change those Profile picture and Display name, I'm trying to update user photo or user name, CompleteListener called, task.isSuccessful = true but nathing done, why?
Function to update name:
FirebaseUser mFirebaseUser = FirebaseAuth.getInstance().getCurrentUser();
final String newName;
newName = input.getText().toString();
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(newName)
.build();
mFirebaseUser.updateProfile(profileUpdates)
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
DatabaseReference mFirebaseDatabaseReference = FirebaseDatabase.getInstance().getReference().child("users");
mFirebaseDatabaseReference.child(mFirebaseUser.getUid()).child("DisplayName").setValue(newName);
updateUI();
Toast.makeText(ProfileActivity.this, "User display name updated.", Toast.LENGTH_SHORT).show();
} else
Toast.makeText(ProfileActivity.this, "Error while updating display name.", Toast.LENGTH_SHORT).show();
}
});
Same when i'm trying to update Profile picture that I just uploaded to Firebase Storage...
And idea?
EDIT:
Sometimes the username really get updated, I think it's take like more then 10 minutes to update, why?
I have had a similar problem where the User information was not updating until the User re-authenticated. I resolved it by also saving this information in my firebase database. For me this made sense, as I wanted Users to be able to get basic information about other Users anyway.
My code ended up looking something like this. When the account is created, or modified, I made a call to the "users/{uid}" endpoint and updated the object there. From here I used the GreenRobot EventBus to send my new User object to whoever was subscribed so that it would be updated on the screen.
private FirebaseUser firebaseUser;
public void createUser(String email, String password, final User user, Activity activity, final View view) {
FirebaseAuth.getInstance().createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(activity, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d(TAG, "createUserWithEmail:onComplete:" + task.isSuccessful());
// If sign in fails, display a messsage to the user. If sign in successful
// the auth state listener will be notified and logic to handle
// signed in user can be handled in the listener
if (!task.isSuccessful()) {
Snackbar.make(view, task.getException().getLocalizedMessage(), Snackbar.LENGTH_SHORT).show();
} else {
firebaseUser = task.getResult().getUser();
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(user.displayName)
.build();
firebaseUser.updateProfile(profileUpdates);
updateDatabase(user);
EventBus.getDefault().post(new LoginEvent());
}
}
});
}
public boolean updateDatabase(User user) {
if (firebaseUser == null) {
Log.e(TAG, "updateDatabase:no currentUser");
return false;
}
return userReference.setValue(user).isSuccessful();
}
The setup of the database watcher was done something like this. Note that you need to make sure that you remove the listener when the User logs out and add a new one when the User logs in.
protected void setupDatabaseWatcher() {
String uid = firebaseUser.getUid();
userReference = FirebaseDatabase.getInstance().getReference("users/" + uid);
userReference.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// This method is called once with the initial value and again
// whenever data at this location is updated.
User user = dataSnapshot.getValue(User.class);
Log.d(TAG, "Value is: " + user);
EventBus.getDefault().post(new UserUpdateEvent(user));
}
#Override
public void onCancelled(DatabaseError error) {
// Failed to read value
Log.w(TAG, "Failed to read value.", error.toException());
}
});
}
Use this simple code:
UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName(newName)
.build();