So I try to create Sign-out button in a fragment Home but, everytime user SignOut, the app will crash and it caused by document snapshot.
this is my document snapshot code.
private fun loadProfile() {
val user = mAuth!!.currentUser
if (user != null) {
...//loading profile code
}
val uid = user.uid
documentReference = db!!.collection("users").document(uid)
documentReference!!.addSnapshotListener { documentSnapshot, e ->
//this if returning null after user signout
if (documentSnapshot!!.exists()) {
if (documentSnapshot.get(KEY_HP) != null) {
...//loading profile code
}else {
val intent = Intent(this#Home.activity, LoginActivity::class.java)
startActivity(intent)
}
}
} else {
val intent = Intent(this#Home.activity, LoginActivity::class.java)
startActivity(intent)
}
}
as you can see I desperately add else everywhere but that does not help.
this is my Sign-out method
private fun signOut() {
mAuth!!.signOut()
val intent = Intent(this#Home.activity, LoginActivity::class.java)
startActivity(intent)
}
So, I need to properly signout without the app being crash
FirebaseAuth.getInstance().signOut();
Use this to signout from Firebase. And then, if you try to perform any action of a logged in user, you won't be able to, you will have to login again.
FUTURE REFERENCE
After fighting with the "sign-out" method for a couple days, I realized that if you have snapshot listeners attached within the activity, make sure you handle and return the error, as stated here, or else you may run into some Permission Errors and crashes.
Related
Can someone explain to me why this simple code is not working properly?
In the beginning I created an object with auth and currentUser
object FirebaseUtils {
val auth: FirebaseAuth = FirebaseAuth.getInstance()
val currentUser: FirebaseUser? = auth.currentUser
}
My login activity function
fun login(){
val email = binding.loginInput.text.toString()
val password = binding.passwordInput.text.toString()
auth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener {
task ->
if (task.isSuccessful){
task.result
Log.d("login_status", "Logged: ${currentUser?.uid}")
startActivity(Intent(this, MainActivity::class.java))
finish()
} else {
Log.d("login_status", task.exception.toString())
}
}
}
Start activity:
lifecycleScope.launch {
delay(2000)
if(currentUser?.uid != null){
Log.d("login_status", "Logged: ${currentUser.uid}")
startActivity(Intent(applicationContext, MainActivity::class.java))
}else{
startActivity(Intent(applicationContext, LoginMainActivity::class.java))
}
finish()
}
And sign out
fun signOut(){
auth.signOut()
startActivity(Intent(this, LoginMainActivity::class.java))
Log.d("login_status", "Signed out: ${currentUser?.uid}")
finish()
}
The problem is that after the first login I can see that logged user is null so my app is crashing (no data for the user). However, after restarting I am logged in as this user. I cant also sign out - activity changes to the login screen but even if I pass different credentials the user remains the same.
EDIT
When I log in and restart the app it works - the correct user is logged.
When I log out and restart the app it also works - the user is signed out.
So why is there a problem between activities
Your result is null because you are getting the result object out of the task object but you aren't doing anything with it. Simply calling:
task.result
Doesn't provide any benefit. However, you can save that result into a variable and call AuthResult#getUser() like this:
val user = task.result.getUser()
Log.d("login_status", "Logged: ${user?.uid}")
So right after you authenticate the user successfully, you'll be able to log the UID correctly.
I've been struggling with this problem for more than a week help me out if you know the fix.
enter image description here
I am trying to delete the data from a child node in the Realtime Firebase but it keeps regenerating even when I delete the token data.
This is my code:
when a user logs in an FCM token is generated automatically (onCreate).
when I try to log him out of his account I want the token to be deleted from the token list but it keeps regenerating even when I logout
this is the User fragment is redirected to after login:
val currentUser : String = firebaseAuth.currentUser?.uid.toString()
val databaseReference = FirebaseDatabase.getInstance("https://trial-38785-default-rtdb.firebaseio.com/")
.getReference("AppUsers").child("Doctor").child(currentUser)
FirebaseMessaging.getInstance().token.addOnCompleteListener {
if (it.isComplete) {
val firebaseToken = it.result.toString()
val myRef =
FirebaseDatabase.getInstance("https://trial-38785-default-rtdb.firebaseio.com/")
.getReference("AppUsers").child("Doctor").child(currentUser)
myRef.child("Token").orderByChild("token").equalTo(firebaseToken)
.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val token : Token = Token(firebaseToken)
if (snapshot.exists()) {
return
} else {
databaseReference.child("Token")
.child(currentdate.toString()).setValue(token)
}
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context, error.message, Toast.LENGTH_LONG).show()
}
})
}
}.addOnFailureListener {
Toast.makeText(context, it.message, Toast.LENGTH_LONG).show()
}
This is another fragment where the doctor logout.
pressing logout button this is the code i ran
val delRef = FirebaseDatabase.getInstance().getReference("AppUsers")
.child("Doctor").child(currentId.toString())
.child("Token").child(key.toString()).removeValue()
delRef.addOnSuccessListener {
println("Removed.")
}.addOnFailureListener {
println("Not Removed.")
}
When using the Query#addValueEventListener() method, it means that you are trying to listen for real-time updates. That being said, every change that takes place in the database and corresponds to your query, always triggers your onDataChange() method. Since when you are logging out, you remove a value from the query you are listening to, your token gets written again, hence that behavior. This is happening over and over again.
To solve this, simply change the above method call to Query#addListenerForSingleValueEvent(). This means that it listen for changes only once.
I finally figured out what I was doing wrong, I just had to move my code (myRef) outside of FirebaseMessaging.
I am doing a college project where students and teachers are using school data. I want teachers and students to have access to different data. I want teachers to send to home activity while students to main Activity. The user has already entered his type while Signing up. I want users to get specific activity based on user type. I am getting user-type data but not able to use it for other work.
Here is my code:
override fun onStart() {
super.onStart()
var usertype: String? = null
if (FirebaseAuth.getInstance().currentUser != null) {
var currentUser: String = FirebaseAuth.getInstance().currentUser.uid
val DataBaseReference = FirebaseDatabase.getInstance().getReference().child("Users")
DataBaseReference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(datasnapshot: DataSnapshot) {
usertype = datasnapshot.child(currentUser).child("type").getValue(String::class.java)
usertype?.let { Log.d("usertype", it) }
}
override fun onCancelled(error: DatabaseError) {
}
})
val utype = usertype.toString()
Log.i("utype", utype)
if (utype == "Student"){
val intent = Intent(this, MainActivity::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
}
else {
val intent = Intent(this, home::class.java)
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
finish()
}
}
}
The Log result of user-type is a student but utype Log result is null. Now I want to get data of usertype from onDatachange to other function.
I am getting user-type data but not able to use it for other work.
This is happening because you are using the following if statement:
val utype = usertype.toString()
Log.i("utype", utype)
if (utype == "Student"){ ...}
Outside the callback. Firebase API is asynchronous. Any code that needs data from an asynchronous operation needs to be inside the "onDataChange()" method, or be called from there. So the simplest solution, in this case, is to move all the logic related to the above lines of code, in the "onDataChange()" method, inside the scope of let:
usertype = datasnapshot.child(currentUser).child("type").getValue(String::class.java)
usertype?.let {
Log.d("usertype", it)
//Use your logic
}
}
in login page, I use these 2 codes below:
facebookLoginButton.setOnClickListener {
facebookLoginManager.logInWithReadPermissions(this, Arrays.asList("email", "public_profile"))
}
private fun setUpFacebookLogin() {
facebookLoginManager.registerCallback(callbackManager, object : FacebookCallback<LoginResult> {
override fun onSuccess(loginResult: LoginResult) {
Log.d(TAG, "facebook:onSuccess:$loginResult")
handleFacebookAccessToken(loginResult.accessToken)
}
override fun onCancel() {
Log.d(TAG, "facebook:onCancel")
progressBar.visibility = View.GONE
}
override fun onError(error: FacebookException) {
Log.d(TAG, "facebook:onError", error)
progressBar.visibility = View.GONE
mActivity.longToast("Gagal masuk dengan akun Facebook, silahkan periksa koneksi internet Anda, atau gunakan akun Google atau email yang lain.")
}
})
}
private fun handleFacebookAccessToken(token: AccessToken) {
Log.d(TAG, "handleFacebookAccessToken:$token")
val credential = FacebookAuthProvider.getCredential(token.token)
auth.signInWithCredential(credential).addOnSuccessListener {
Log.d(TAG, "facebook signInWithCredential:success")
val user = auth.currentUser ?: return#addOnSuccessListener
checkUserBasicDataInFirestore(user)
}.addOnFailureListener {exception ->
progressBar.visibility = View.GONE
Log.d(TAG, "facebook signInWithCredential failed: ${exception.localizedMessage}")
mActivity.longToast(exception.localizedMessage)
}
}
I actually can register the user to firebase auth by using those code, but I have problem when trying to login using facebook. auth.signInWithCredential(credential).addOnSuccessListener is triggered actually, so I assume I can success login using facebook in login page
but in the other screen (fragment), when I want to access the current user to get the uid, it always null, like this
val userFirebaseAuth = FirebaseAuth.getInstance().currentUser
I don't understand why userFirebaseAuth is always null ?
I was running into a similar issue so posting this here in case it is helpful to someone else. Even though the user was already logged in through facebook login, Firebase.auth.currentUser was always null for me. I realized that I was accidentally using facebook login by itself rather than through firebase. I was able to get the correct login state of facebook by doing this:
val accessToken = AccessToken.getCurrentAccessToken()
val isLoggedIn = accessToken != null && !accessToken.isExpired
The way you would notify firebase about your facebook login after successfully signing in through facebook is the following. (Copied ofhttps://firebase.google.com/docs/auth/android/facebook-login#authenticate_with_firebase)
private fun handleFacebookAccessToken(token: AccessToken) {
Log.d(TAG, "handleFacebookAccessToken:$token")
val credential = FacebookAuthProvider.getCredential(token.token)
auth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
// Sign in success, update UI with the signed-in user's information
Log.d(TAG, "signInWithCredential:success")
val user = auth.currentUser
updateUI(user)
} else {
// If sign in fails, display a message to the user.
Log.w(TAG, "signInWithCredential:failure", task.exception)
Toast.makeText(baseContext, "Authentication failed.",
Toast.LENGTH_SHORT).show()
updateUI(null)
}
}
}
After doing this, Firebase.auth.currentUser will be populated!
From my experience, the current user isn't always immediately available at the app launches from a cold start. It can take a moment for Firebase Authentication to initialize and refresh the user's ID token if necessary. Since you don't know how long that will take, you should use a listener to determine the first possible moment when the current user is absolutely known and verified by the SDK. It's a good idea to read about: How does the firebase AuthStateListener work?
Create a new AuthStateListener:
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// Sign in logic here.
}
}
};
Then add and remove it as needed:
FirebaseAuth.getInstance().addAuthStateListener(mAuthListener);
FirebaseAuth.getInstance().removeAuthStateListener(mAuthListener);
Also check out the API documentation on AuthStateListener.
So I setup email/password register and login.
That is working. I thought Firebase took care of this but apparently not.
I want, after the user closes the app, to be logged in already next time they open the app.
What is missing?
class LoginActivity : AppCompatActivity(){
lateinit var auth: FirebaseAuth
lateinit var user: FirebaseAuth
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
auth = FirebaseAuth.getInstance()
}
fun loginLoginClicked(view: View) {
// Perform login
val email = loginEmailTxt.text.toString()
val password = loginPasswordTxt.text.toString()
auth.signInWithEmailAndPassword(email, password)
.addOnSuccessListener {
finish()
}
.addOnFailureListener { exception ->
Log.e("Exception", "Could not sign in user - ${exception.localizedMessage}")
}
val loginIntent = Intent(this, MainActivity::class.java)
startActivity(loginIntent)
}
fun loginCreateClicked(view: View) {
// segue to the create user activity
val createIntent = Intent(this, SignUpActivity::class.java)
startActivity(createIntent)
}}
}
Firebase Authentication does automatically remember authentication state, so the user will still be authenticated when the app is restarted.
However, if your LoginActivity is the launcher activity, you'll still land on this activity, so you'll need to check whether the user is authenticated in onCreate(), and then redirect them to your MainActivity if they are already logged in, something like:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
auth = FirebaseAuth.getInstance();
if (auth.getCurrentUser() != null) {
// User is signed in (getCurrentUser() will be null if not signed in)
val intent = Intent(this, MainActivity::class.java);
startActivity(intent);
finish();
}
}
This makes use of the FirebaseAuth#getCurrentUser() method that will return a FirebaseUser object if the user is logged in, or null if they are not logged in.
Alternatively, you could swap it around so that the MainActivity is the launcher activity and then only show your LoginActivity if the user is not logged in.
....
If anyone landing up here for achieving same thing using Java then use following code (credit to Grimthorr's answer for the Kotlin version that this is a port of)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
auth = FirebaseAuth.getInstance();
if (auth.getCurrentUser() != null) {
// User is signed in (getCurrentUser() will be null if not signed in)
Intent intent = Intent(this, MainActivity.class);
startActivity(intent);
finish();
// or do some other stuff that you want to do
}