How to manage Login Validation with Room in Kotlin? - android

I'm trying to make work the Login Validation form, but the program stops then reaches the second if, and I have Invalid email output. Run is clear and out of mistakes. Can't figure out what I'm doing wrong and why emailList is null
private fun logIn() {
val email = binding.editEmailAddress.text.toString()
val password = binding.editPassword.text.toString()
if (inputCheck(email, password)) {
mLoginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
val emailList = mLoginViewModel.getUserEmail(email)
if (emailList != null) {
if (emailList.password == password) {
Toast.makeText(requireContext(), "Logged in as $email", Toast.LENGTH_LONG)
.show()
findNavController().navigate(R.id.action_loginFragment_to_listFragment)
} else {
Toast.makeText(requireContext(), "Invalid password", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(requireContext(), "Invalid email", Toast.LENGTH_SHORT).show()
}
} else {
Toast.makeText(requireContext(), "Fill out blank fields", Toast.LENGTH_LONG).show()
}
}
private fun inputCheck(email: String, password: String): Boolean {
return !(TextUtils.isEmpty(email) || TextUtils.isEmpty(password))
}
LoginViewModel
fun getUserEmail(email: String): User? {
var checker: User? = null
viewModelScope.launch(Dispatchers.IO) {
checker = repository.getUserEmail(email)
}
return checker
}

fun getUserEmail(email: String): User? {
var checker: User? = null
viewModelScope.launch(Dispatchers.IO) {
checker = repository.getUserEmail(email)
}
return checker
// Will return null always because this is not waiting to assign value by above repository method
}
Insted you can do this
suspend fun getUserEmail(email: String): User? {
return repository.getUserEmail(email)
}
And in Activity Or Fragment
mLoginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
lifecycleScope.launch {
val emailList = mLoginViewModel.getUserEmail(email)
}
Don't Know what error you are getting on above code if that not works then use below code
mLoginViewModel = ViewModelProvider(this)[LoginViewModel::class.java]
CoroutineScope(Dispatchers.IO).launch {
val emailList = mLoginViewModel.getUserEmail(email)
withContext(Dispatchers.Main){
//Do whatever with emailList
}
}

Related

Why is the user details vanishes from the Firestore?

I am using Firebase PhoneAuth for user registration with my app. Users are verified successfully and started using the app without any issues. But, It happened to me a couple of times that user details vanished from Firestore database, not all users details have gone though. When I check the collection users in the Firestore, the document does exist for the user but some data was gone, in fact, it's overwritten the old data as if this is new user registration (However, the User UID under the Authentication is remain same). For example, the default value is user_type = "customer", as you can see in the fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential). Later I change this value accordingly to my need and when this issue happens the changes I made to this field and other fields are changed back to the default values.
Following is the code in my SignInWithPhone which will be called from the SplashScreen
class SigninWithPhoneActivity: BaseActivity() {
private lateinit var binding: ActivitySignInWithPhoneBinding
private lateinit var mAuth: FirebaseAuth
var code = ""
var number = ""
var phoneNumber = ""
var storedOtpID = ""
private lateinit var resendToken: PhoneAuthProvider.ForceResendingToken
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySignInWithPhoneBinding.inflate(layoutInflater)
setContentView(binding.root)
}
mAuth = FirebaseAuth.getInstance()
binding.btnGetOtp.setOnClickListener {
code = binding.etCountryCode.text.toString().trim()
number = binding.etMobileNumber.text.toString().trim()
phoneNumber = code + number
if (number.isNotEmpty()) {
binding.etMobileNumber.isEnabled = false
binding.flOtp.visibility = View.VISIBLE
binding.btnGetOtp.visibility = View.GONE
binding.btnSignIn.visibility = View.VISIBLE
sendVerificationCode(phoneNumber)
} else {
Toast.makeText(this, "Please enter a valid mobile number", Toast.LENGTH_LONG).show()
}
}
binding.btnSignIn.setOnClickListener {
val otp = binding.etOtp.text.toString().trim()
if (otp.isNotEmpty() || otp.length != 6) {
verifyVerificationCode(otp)
} else {
Toast.makeText(this, "Please enter the OTP received through SMS", Toast.LENGTH_LONG)
.show()
}
}
}
private val mCallBack: PhoneAuthProvider.OnVerificationStateChangedCallbacks =
object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
override fun onVerificationCompleted(credential: PhoneAuthCredential) {
val code = credential.smsCode
if (code != null) {
binding.etOtp.setText(code)
binding.pbOtp.visibility = View.GONE
}
}
override fun onVerificationFailed(p0: FirebaseException) {
binding.etMobileNumber.isEnabled = true
binding.flOtp.visibility = View.GONE
binding.btnGetOtp.visibility = View.VISIBLE
binding.btnSignIn.visibility = View.GONE
Toast.makeText(this#SigninWithPhoneActivity, "Login Failed", Toast.LENGTH_LONG)
.show()
}
override fun onCodeSent(otpID: String, token: PhoneAuthProvider.ForceResendingToken) {
super.onCodeSent(otpID, token)
Toast.makeText(
this#SigninWithPhoneActivity,
"OTP is send to your number",
Toast.LENGTH_LONG
).show()
storedOtpID = otpID
resendToken = token
}
}
private fun sendVerificationCode(phoneNumber: String) {
binding.pbOtp.visibility = View.VISIBLE
Toast.makeText(this#SigninWithPhoneActivity, "Sending OTP", Toast.LENGTH_LONG).show()
val options = PhoneAuthOptions.newBuilder(mAuth!!)
.setPhoneNumber(phoneNumber)
.setTimeout(60L, TimeUnit.SECONDS)
.setActivity(this)
.setCallbacks(mCallBack)
.build()
PhoneAuthProvider.verifyPhoneNumber(options)
}
private fun verifyVerificationCode(code: String) {
Toast.makeText(
this#SigninWithPhoneActivity,
"Verifying credentials",
Toast.LENGTH_LONG
).show()
val credential = PhoneAuthProvider.getCredential(storedOtpID, code)
signInWithPhoneAuthCredential(credential)
}
private fun signInWithPhoneAuthCredential(credential: PhoneAuthCredential) {
mAuth.signInWithCredential(credential)
.addOnCompleteListener(this) { task ->
if (task.isSuccessful) {
val firebaseUser: FirebaseUser = task.result!!.user!!
val userPreliminaryDetails = User(
firebaseUser.uid,
user_type = "customer",
mobile = binding.etMobileNumber.text.toString()
)
FirestoreClass().checkIfUserAlreadyExist(
this#SigninWithPhoneActivity,
firebaseUser.uid,
userPreliminaryDetails
)
} else {
showErrorSnackBar(task.exception!!.message.toString(), true)
if (task.exception is FirebaseAuthInvalidCredentialsException) {
binding.etMobileNumber.isEnabled = true
binding.flOtp.visibility = View.GONE
binding.etOtp.text?.clear()
binding.btnGetOtp.visibility = View.VISIBLE
binding.btnGetOtp.text = "Resend OTP"
binding.btnSignIn.visibility = View.GONE
Toast.makeText(this, "OTP entered is wrong", Toast.LENGTH_LONG).show()
}
}
}
}
fun userRegistrationSuccess() {
finish()
startActivity(Intent(this, ServiceAreaActivity::class.java))
Toast.makeText(this, "Signed Up successful", Toast.LENGTH_LONG).show()
}
fun userSignInSuccess() {
finish()
startActivity(Intent(this, ServiceAreaActivity::class.java))
Toast.makeText(this, "Signed in successfully", Toast.LENGTH_LONG).show()
}
}
EDIT:
The following function is called to check if the user already exists and accordingly sign in or register a new user.
fun checkIfUserAlreadyExist(
activity: SigninWithPhoneActivity, userId: String, userDetails: User
) {
mFireStore.collection("users")
.whereEqualTo(Constants.USER_ID, userId)
.get()
.addOnSuccessListener { document ->
if (document.documents.size > 0) {
activity.userSignInSuccess()
} else {
FirestoreClass().registerUser(activity, userDetails)
}
}
.addOnFailureListener { e ->
}
}
private fun registerUser(activity: SigninWithPhoneActivity, userInfo: User) {
mFireStore.collection(Constants.USERS)
.document(userInfo.user_id)
.set(userInfo, SetOptions.merge())
.addOnSuccessListener {
activity.userRegistrationSuccess()
}
.addOnFailureListener { e ->
activity.hideProgressDialog()
}
}

Probem while reading field in document from firestore in kotlin

private fun logInRegisteredUser() {
if (validateLoginDetails()) {
// Show the progress dialog.
showProgressDialog(resources.getString(R.string.please_wait))
// Get the text from editText and trim the space
val email = et_email.text.toString().trim { it <= ' ' }
val password = et_password.text.toString().trim { it <= ' ' }
// Log-In using FirebaseAuth
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
// Hide the progress dialog
hideProgressDialog()
if (task.isSuccessful) {
val currrentUser= FirebaseAuth.getInstance().currentUser
/* if (currrentUser != null) {
val washingtonre = mFireStore.collection("use").document(currrentUser.uid)
washingtonre.get().addOnSuccessListener(object: OnSuccessListener<DocumentSnapshot> {
override fun onSuccess(documentSnapshot:DocumentSnapshot) {
if (documentSnapshot.exists())
{
var arrList = ArrayList<String>()
arrList = documentSnapshot.get("organization") as ArrayList<String>
Toast.makeText(applicationContext,"$arrList",Toast.LENGTH_SHORT).show()
for (i in 0 until arrList.size)
{
//Traversing the list
}
}
}
})
}*/
if (currrentUser != null) {
val c = FirebaseFirestore.getInstance()
val d = c.collection("users").document(currrentUser.uid)
d.get().addOnSuccessListener { document ->
if (document != null) {
arrList = document.get("organization") as ArrayList<String>
if (arrList.size>1) {
val intent = Intent(this#LoginActivity, companyDetails::class.java)
intent.putStringArrayListExtra("key", arrList)
startActivity(intent)
Toast.makeText(applicationContext, "$arrList", Toast.LENGTH_SHORT).show()
}
else if (arrList.size<=1) {
val intent = Intent(this#LoginActivity, MainActivity::class.java)
startActivity(intent)
}
} else {
Log.d("Tag", "No such document")
}
}
.addOnFailureListener { exception ->
Log.d("TAG", "get failed with ", exception)
}
}
} else {
showErrorSnackBar(task.exception!!.message.toString(), true)
}
}
}
}
This is my code to validate login details. When login button is pressed this code is executed.
Now I want to check an array field named 'organization' in firestore to check wheather the user has two or more companies to his credits. Now when I run the app if organization field doesnt exist my app crashes. I want the app to work even if the organization field doesnt exist. How to change the codes for that?enter image description here

How to clear the data of a LiveData Object after a Success response in Retrofit Kotlin

I created a request inside a viewModel for adding a new Contact, and I use a LiveData object to store the data from the response to check different things inside my Fragment. My problem is, how can I clear the object after adding a contact? After adding a new contact successfully, when I press the Add Button again even if I don't fill any field, the success function in my fragment is called. I think this is because of the last item added so I need to clear the data. Is that possible? Thanks.
ViewModel class:
val contactNew: LiveData<Resource<UserModel>>
get() = _contactNew
private val _contactNew = MutableLiveData<Resource<UserModel>>()
fun addNewContact(
phoneNumber: String,
username: String,
firstName: String,
lastName: String,
email: String,
birthdate: String,
otherEvent: String,
eventDate: String
) {
var addedContact = UserModel(
username = username,
phone = phoneNumber,
firstName = firstName,
lastName = lastName,
email = email,
birthday = birthdate,
eventTitle = otherEvent,
eventDate = eventDate
)
_isLoading.value = true
disposable.add(
contactsAndGroupsRepository.postAddContact(
handlerId = SharedPreferencesHelper.handlerId,
contactNew = addedContact
)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : DisposableSingleObserver<UserModel>() {
override fun onSuccess(response: UserModel) {
_isLoading.value = false
_contactNew.value = Resource.success(response)
Log.e(TAG, response.id)
}
override fun onError(e: Throwable) {
_isLoading.value = false
_contactNew.value = Resource.error(e.message)
Log.e(TAG, e.message.toString())
}
}
)
)
}
AddContactFragment
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (item.itemId == R.id.add_contact_action) {
activity?.hideKeyboard()
addContact()
observeAddContactResponse()
}
return super.onOptionsItemSelected(item)
}
private fun addContact() {
viewModel.isLoading.observe(viewLifecycleOwner, {
binding.isLoading = it ?: true
})
val phoneNumber = binding.countryCodePicker.fullNumberWithPlus.trim()
val username = binding.username.text!!.trim().toString()
val firstName = binding.firstName.text!!.trim().toString()
val lastName = binding.lastName.text!!.trim().toString()
val email = binding.emailAddress.text!!.trim().toString()
val birthdate = formatDate(binding.birthdate.text!!.trim().toString())
val otherEvent = binding.eventTitle.text!!.trim().toString()
val eventDate = formatDate(binding.eventDate.text!!.trim().toString())
if (binding.phoneNumber.text!!.isNotEmpty() && username.isNotEmpty() && firstName.isNotEmpty() && lastName.isNotEmpty()) {
if (isPhoneNumberValid(
binding.countryCodePicker.fullNumberWithPlus,
binding.countryCodePicker.selectedCountryCode
)
) {
if (email.isNullOrEmpty() || Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
if ((birthdate.isNullOrEmpty()) || (birthdayInMilliseconds < Calendar.getInstance().timeInMillis)) {
viewModel.addNewContact(
phoneNumber,
username,
firstName,
lastName,
email,
birthdate,
otherEvent,
eventDate
)
} else {
showDialog(R.string.something_wrong, R.string.invalid_date)
}
} else
showDialog(R.string.something_wrong, R.string.invalid_email)
} else {
showDialog(R.string.something_wrong, R.string.invalid_phone_number)
}
} else {
this.showToast(resources.getString(R.string.fill_required_info), Toast.LENGTH_SHORT)
}
}
private fun observeAddContactResponse() {
viewModel.contactNew.observe(viewLifecycleOwner, { contactNew ->
contactNew?.let {
if (it.status == Status.SUCCESS) {
binding.setPictureButton.visibility = View.VISIBLE
hideMenu()
showDialog(R.string.success, R.string.contact_added_successfully)
} else {
if (it.message!!.contains("409")) {
if (!isDialogShown) {
isDialogShown = true
showDialog(R.string.something_wrong, R.string.contact_exists)
}
} else
if (!isDialogShown) {
isDialogShown = true
showDialog(R.string.something_wrong, R.string.something_wrong_message)
}
}
}
})
}
Because you call observeAddContactResponse() after addContact() with checking whether its valid or not , then in observeAddContactResponse() you checked if the call to the server response will be successful or not , and its seem to be successful here , what lead to act as if its was a valid data , what you should do is to call observeAddContactResponse() within last if statement in addContact() .
Just remove observeAddContactResponse from onOptionsItemSelected and replace it in onCreate
This is happening because you set an Observer for contactNew each time you trigger the addButton

Kotlin class is returning false before it has finished

In my main activity i have a function that runs once the login button it pressed. I'm calling a class that attempts to login via an API which takes a second or two to run. However, when i'm calling the login class it seems to be threaded and doesn't wait for the login to complete and returns false which is the default. Example code is as follows:
class MainActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun loginBtnClicked(view: View) {
progressBar.visibility = View.VISIBLE
// get domain info
val domain: TextView = findViewById<TextView>(R.id.loginDomain)
val domainUrl = domain.text.toString()
val url = "$domainUrl/api/"
// get username info
val username = loginUsername
// get password info
val password = loginPassword
if (ApiGet(
url = url,
username = username.text.toString(),
password = password.text.toString()
).login()) {
println("It worked")
val dashboard = Intent(this, DashboardActivity::class.java)
Consts.DOMAIN = url
Consts.USERNAME = username.text.toString()
Consts.PASSWORD = password.text.toString()
startActivity(dashboard)
} else {
println("It didn't work")
progressBar.visibility = View.INVISIBLE
runOnUiThread {
Log.i(ContentValues.TAG, "runOnUiThread")
Toast.makeText(
applicationContext,
"Please check the domain, username and password then try again.",
Toast.LENGTH_SHORT
).show()
}
}
}
}
class ApiGet(val url: String, val username: String = Consts.USERNAME, val password: String = Consts.PASSWORD) {
fun login(): Boolean {
var loginAttempt: Boolean = false
var apiData: ApiLogin
val creds = Credentials.basic(username, password)
val request = Request.Builder().url(url).header("Authorization", creds).build()
val client = OkHttpClient()
client.newCall(request).enqueue(
object : Callback {
override fun onResponse(call: Call, response: Response) {
val body: String? = response.body()?.string()
val gson: Gson = GsonBuilder().create()
apiData = gson.fromJson(body, ApiLogin::class.java)
if (apiData.detail == "Invalid username/password.") {
loginAttempt = false
println(loginAttempt)
} else {
loginAttempt = true
println(loginAttempt)
}
}
override fun onFailure(call: Call, e: IOException) {
Toast.makeText(
MainActivity().getApplicationContext(),
"Please check your connection and try again.",
Toast.LENGTH_SHORT
).show()
loginAttempt = false
}
})
return loginAttempt
}
class ApiLogin(val detail: String)
}
It is because your newCall method runs async to the main thread meaning the rest of your code keeps running after you call it while it waits on another thread. To fix this rather than returning your result you can handle it in a callback like so:
class ApiGet(val url: String, val username: String = Consts.USERNAME, val password: String = Consts.PASSWORD) {
fun login(completion: (Boolean)->Unit) {
var loginAttempt: Boolean = false
var apiData: ApiLogin
val creds = Credentials.basic(username, password)
val request = Request.Builder().url(url).header("Authorization", creds).build()
val client = OkHttpClient()
client.newCall(request).enqueue(
object : Callback {
override fun onResponse(call: Call, response: Response) {
val body: String? = response.body()?.string()
val gson: Gson = GsonBuilder().create()
apiData = gson.fromJson(body, ApiLogin::class.java)
if (apiData.detail == "Invalid username/password.") {
println(loginAttempt)
completion(False)
} else {
print(loginAttempt)
completion(True)
}
}
override fun onFailure(call: Call, e: IOException) {
Toast.makeText(
MainActivity().getApplicationContext(),
"Please check your connection and try again.",
Toast.LENGTH_SHORT
).show()
completion(False) }
})
}
You can call the login function like so:
ApiGet(url = url,
username = username.text.toString(),
password = password.text.toString()).login { result ->
if (result) {
// success
} else {
// failure
}
}

how can i insert CALLBACK code in my code

When I created JOIN Action and LOGIN Action on Android app,
Aa problem has occurred.
Working with MVP pattern in LOGIN Action.
But the results on the logins aren't exactly what I want.
I'll show you the code.
class LoginModel {
var TAG = "LoginModel"
private var ID: String
private var PW: String
var resultTxt: String = ""
var auth: FirebaseAuth = FirebaseAuth.getInstance()
constructor(ID: String, PW: String) {
this.ID = ID
this.PW = PW
}
fun login(ID: String, PW: String) : String{
this.ID = ID
this.PW = PW
auth.signInWithEmailAndPassword(ID, PW)
.addOnCompleteListener { task ->
//
if (task.isSuccessful) {
val user = auth.currentUser
resultTxt = "Login Success"
} else {
resultTxt = "Login Failed"
}
}
return resultTxt
// I'd like to process the results based on the return.
// But it doesn't return the way I want it.
// I know it's related to asynchronous processing.
// So where should I put the callback function, and how should I write
// it?
}
}
Add a callback to your login function which get invoked after resultTxt has been set. Something along the following lines should work,
class LoginModel {
var TAG = "LoginModel"
private var ID: String
private var PW: String
var resultTxt: String = ""
var auth: FirebaseAuth = FirebaseAuth.getInstance()
constructor(ID: String, PW: String) {
this.ID = ID
this.PW = PW
}
fun login(ID: String, PW: String, callback: (String)->Unit) {
this.ID = ID
this.PW = PW
auth.signInWithEmailAndPassword(ID, PW)
.addOnCompleteListener { task ->
//
if (task.isSuccessful) {
val user = auth.currentUser
resultTxt = "Login Success"
} else {
resultTxt = "Login Failed"
}
//The callback get's invoked with your expected result value.
callback.invoke(resultTxt)
}
//Don't return here
//return resultTxt
// I'd like to process the results based on the return.
// But it doesn't return the way I want it.
// I know it's related to asynchronous processing.
// So where should I put the callback function, and how should I write
// it?
}
}
You can then call the method using,
login(id,password) { result ->
//Do what you want with the result here
}
fun login(ID: String, PW: String, callback:(String) -> Unit) {
this.ID = ID
this.PW = PW
auth.signInWithEmailAndPassword(ID, PW)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
val user = auth.currentUser
resultTxt = "Login Success"
} else {
resultTxt = "Login Failed"
}
callback.invoke(resultTxt)
}
}
try this as a callback, it will return the value of resultTxt after it has executed.
Then, when you call the login method:
login(ID,PW){ result ->
//here, result is the value of the callback
}
ALTERNATIVELY
You can return the result and user of the call in a callback, like this:
fun login(ID: String, PW: String, callback:(Boolean, User?) -> Unit) {
this.ID = ID
this.PW = PW
auth.signInWithEmailAndPassword(ID, PW)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
callback.invoke(true, auth.currentUser)
} else {
callback.invoke(false, null)
}
}
}
Then you can use it like this :
Login(id, password){ result: Boolean, user: User? ->
if(result){
//the result is successful
user?.let{ authUser ->
//here, authUser is your user
}
} else{
//the result was not successful
}
}

Categories

Resources