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
}
}
Related
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
}
}
I'm trying to register a customer through Firebase Realtime-Database, but I'm needing help adding a stripeID to the customer reference through a Cloud Function. I can register a customer just fine, but how can I append my Cloud Function when I click the sign up action because I'm not getting anything in the Firebase Function Logs right now? Thank you.
Cloud Function
exports.createStripeCustomer = functions.https.onCall( async (data, context) => {
const email = data.email
const uid = context.auth.uid
console.log(uid)
if (uid === null) {
console.log('Illegal access attempt due to unauthenticated attempt.')
throw new functions.https.HttpsError('internal', 'Illegal access attempt')
}
return stripe.customers
.create({ email: email })
.then((customer) => {
return customer["id"];
})
.then((customerId) => {
admin.database().ref("customers").child(uid).update({
stripeId: customerId,
email: email,
id: uid
})
})
.catch((err) => {
console.log(err);
throw new functions.https.HttpsError(
"internal",
" Unable to create Stripe user : " + err
);
});
})
WelcomeActivity.kt
signUpButton.setOnClickListener {
val registerStub = findViewById<ViewStub>(R.id.registerStub)
registerStub.inflate()
performRegister()
}
private fun performRegister() {
// MARK: - Register ViewStub Properties
val registerViewStubProfileImageButton = findViewById<ImageButton>(R.id.addPhotoImageButton)
val registerViewStubFullnameTextView = findViewById<TextInputEditText>(R.id.fullnameInputTextView)
val registerViewStubUsernameTextView = findViewById<TextInputEditText>(R.id.usernameTextInputView)
val registerViewStubEmailTextView = findViewById<TextInputEditText>(R.id.emailInputTextView)
val registerViewStubPasswordTextView = findViewById<TextInputEditText>(R.id.passwordTextInputView)
val registerViewStubConfirmPasswordTextView = findViewById<TextInputEditText>(R.id.confirmPasswordTextInputView)
val registerViewStubCancelButton = findViewById<Button>(R.id.secondCancelButton)
val registerViewStubSignUpButton = findViewById<Button>(R.id.secondSignUpButton)
val fullnameText = TextUtils.isEmpty(registerViewStubFullnameTextView.text)
val usernameText = TextUtils.isEmpty(registerViewStubUsernameTextView.text)
val emailText = TextUtils.isEmpty(registerViewStubEmailTextView.text)
val passwordText = TextUtils.isEmpty(registerViewStubPasswordTextView.text)
val confirmPasswordText = TextUtils.isEmpty(registerViewStubConfirmPasswordTextView.text)
registerViewStubSignUpButton.setOnClickListener {
FirebaseAuth.getInstance().createUserWithEmailAndPassword(registerViewStubEmailTextView.text.toString(), registerViewStubPasswordTextView.text.toString())
.addOnCompleteListener {
if (!it.isSuccessful) return#addOnCompleteListener
// else if successful
uploadImageToFirebaseStorage()
}
.addOnFailureListener {
Toast.makeText(this, "Failed to create user: ${it.message}", Toast.LENGTH_SHORT).show()
}
}
}
private fun uploadImageToFirebaseStorage() {
if (selectedPhotoUri == null) return
val filename = UUID.randomUUID().toString()
val ref = FirebaseStorage.getInstance().getReference("/customer_profile_images/$filename")
ref.putFile(selectedPhotoUri!!)
.addOnSuccessListener {
ref.downloadUrl.addOnSuccessListener {
saveCustomerToFirebaseDatabase(it.toString())
}
}
}
private fun saveCustomerToFirebaseDatabase(profileImageUrl: String) {
val registerStub = findViewById<ViewStub>(R.id.registerStub)
val registerViewStubFullnameTextView = findViewById<TextInputEditText>(R.id.fullnameInputTextView)
val registerViewStubUsernameTextView = findViewById<TextInputEditText>(R.id.usernameTextInputView)
val registerViewStubEmailTextView = findViewById<TextInputEditText>(R.id.emailInputTextView)
val uid = FirebaseAuth.getInstance().uid ?: ""
val ref = FirebaseDatabase.getInstance().getReference("/customers/$uid")
val customer = Customer(uid, registerViewStubFullnameTextView.text.toString(), registerViewStubUsernameTextView.text.toString(),
registerViewStubEmailTextView.text.toString(), profileImageUrl)
ref.setValue(customer)
.addOnSuccessListener {
uploadStripeCustomer()
registerStub.alpha = 0f
finish()
}
}
private fun uploadStripeCustomer(): Task<String> {
val registerViewStubEmailTextView = findViewById<TextInputEditText>(R.id.emailInputTextView)
val data = hashMapOf(
"email" to registerViewStubEmailTextView
)
return functions
.getHttpsCallable("createStripeCustomer")
.call(data)
.continueWith { task ->
val result = task.result?.data as Map<*, *>
result["createStripeCustomer"] as String
}
}
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
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
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
}
}