SharedPreferences check if data already exist - android

i have a problem here, i want to make if same data or value already exists in sharedpreferences it show toast if this data already exist, so i must input other value. i try to check it with contains but everytime i type same value it keeps replacing the same value
this is the code
binding.btnContinue.setOnClickListener {
dataExist()
saveData()
}
}
private fun saveData() {
val pref = this.getSharedPreferences(Data.Preferences.PREF_NAME, MODE_PRIVATE)
val fullName = binding.etFullName.text.toString()
val jobPref = binding.etJob.text.toString()
val emailPref = binding.etEmailSignUp.text.toString()
val passPref = binding.etPassSignUp.text.toString()
pref.edit {
putString(Data.Preferences.PREF_FULLNAME, fullName)
putString(Data.Preferences.PREF_JOB, jobPref)
putString(Data.Preferences.PREF_EMAIL, emailPref)
putString(Data.Preferences.PREF_PASS, passPref)
apply()
}
val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)
}
private fun dataExist(): Boolean {
val pref = this.getSharedPreferences(Data.Preferences.PREF_NAME, MODE_PRIVATE)
val checkName = pref.getString(Data.Preferences.PREF_NAME, "")
val checkJob = pref.getString(Data.Preferences.PREF_JOB, "")
if (pref.contains(checkName)){
Toast.makeText(this, "Name already exist", Toast.LENGTH_SHORT).show()
return true
}else{
return false
}
}

you should check key existence, not value
boolean hasKey = pref.contains(Data.Preferences.PREF_NAME)
and also respect return of dataExist method
if (!dataExist()) saveData() // save only when not exists
edit - working example
if (pref.contains(Data.Preferences.PREF_NAME)){
val checkName = pref.getString(Data.Preferences.PREF_NAME, null)
val fullName = binding.etFullName.text.toString()
// returns true when key exists and value stored under this key
// is exactly same as entered in EditText
if (fullName.equals(checkName)) return true
}
return false // no key at all, so no data stored

You are currently calling contains on the value. You need to call contains on the key if you want to ensure whether the key exists or not.
if (pref.contains(Data.Preferences.PREF_NAME)){
Toast.makeText(this, "Name already exist", Toast.LENGTH_SHORT).show()
return true
}else{
return false
}

Related

Can't change variable in handler.post method

This is my first post on StackOverflow, so please don't kill me for my poor formatting.
I'm trying to make a Work Tracker App, which logs your time of arrival and time of leave in a MySQL database when you press the button in the app.
I want the app to open the correct (is working / is not working) screen when you launch the app, and I kinda managed to make it work with shared preferences, but I figured it would be more reliable if it would request the status from the database.
The table holding the logs looks like this:
user_id | time_of_arrival | time_of_leave
if the user is still in work, there will be a row where he has time_of_arrival, but the time_of_leave field is NULL.
That's what I want to request here:
private fun checkWorking(
sharedPreferences: SharedPreferences,
localContext: Context
) : Boolean {
val userId = sharedPreferences.getString("userId", "").toString()
var isWorking = false
if (userId != "") {
val handler = Handler(Looper.getMainLooper())
handler.post {
val field = arrayOfNulls<String>(1)
field[0] = "user_id"
val data = arrayOfNulls<String>(1)
data[0] = userId
val putData = PutData(
Database().host + Database().databaseName + "checkWorking.php",
"POST",
field,
data
)
if (putData.startPut()) {
if (putData.onComplete()) {
val result = putData.result
if(result == "You are working") {
isWorking = true
}
}
}
}
}
return isWorking
}
here is the php part:
<?php
require "DataBase.php";
$db = new DataBase();
if ($db->dbConnect()) {
if($db->checkWorking("logs", $_POST['user_id'])) {
echo "Success";
} else echo "Failure";
}
?>
and
function checkWorking($table, $userId) {
$userId = $this->prepareData($userId);
$this->sql = "SELECT * FROM " . $table . " WHERE user_id = '" . $userId . "' AND time_of_leave IS NULL";
$result = mysqli_query($this->connect, $this->sql);
if(mysqli_num_rows($result) != 0) {
return true;
}
return false;
}
(The PHP part works correctly, I just wanted to give full insight about my problem)
My problem is that it always returns false, because I read somewhere that the return finishes faster than the handler.post changing the isWorking variable to true.
How can I fix this issue, I legitimately can't figure out anything else I could try.
Thanks in advance!
yes, the return statement is being called before the handler is done since it will be working on a different thread while the return is still on the main thread.
So, you can solve that by using an interface to return the callback whenever it has been received, first you create the interface as follows:
public interface CallbackListener<T> {
void onSuccess(T response);
}
then you have to modify you method to take this interface as a parameter
private fun checkWorking(
sharedPreferences: SharedPreferences,
localContext: Context,
callback: CallbackListener<Boolean>) {
val userId = sharedPreferences.getString("userId", "").toString()
var isWorking = false
if (userId != "") {
CoroutineScope(IO).launch { //running code on background thread
val field = arrayOfNulls<String>(1)
field[0] = "user_id"
val data = arrayOfNulls<String>(1)
data[0] = userId
val putData = PutData(
Database().host + Database().databaseName + "checkWorking.php",
"POST",
field,
data
)
if (putData.startPut()) {
if (putData.onComplete()) {
val result = putData.result
withContext(Main) {//returning to main thread
if (result == "You are working") {
callback.onSuccess(true)
} else
callback.onSuccess(false)
}
}
}
}
I used kotlin Coroutines here instead of handler, but it can be applied to both of them.
then you can call your new method as follows:
checkWorking(
sharedPreferences,
context,
object: CallbackListener<Boolean>{
override fun onSuccess(response: Boolean?) {
//insert your logic here
}
}
)

Is there a way to check a firestore document if its certain field is equivalent to some value?

I have a function where I validate a product as you can see in this snippet:
private fun validateProduct() : Boolean{
val newProductName = binding.etNewProductName.text.toString().trim()
val newProductPrice = binding.etNewProductPrice.text.toString().trim()
val newProductCategory = binding.spNewProductCategory.selectedItem.toString()
return when{
TextUtils.isEmpty(newProductName) -> {
showErrorSnackBar(binding.root, "Product name cannot be empty.", true)
false
}
TextUtils.isEmpty(newProductPrice) -> {
showErrorSnackBar(binding.root, "Price cannot be empty.", true)
false
}
//make sure the first element is not a valid category
newProductCategory == binding.spNewProductCategory.getItemAtPosition(0) -> {
showErrorSnackBar(binding.root, "Please select a valid category.", true)
false
}
//check if the new product's name already exists in the Firestore collection.
//if so, return false.
else -> {
true
}
}
}
Edit:
My logic in mind was to iterate over the documents. Check each document if document["name"].toString() == newProductName if so, return false and display an error snackbar.
Is there a way to check a Firestore document if its certain field is equivalent to some value?
Sure, there is. As you already said, yes, you have to iterate over the collection, but not for getting all documents and checking the new product name on the client. You have to do that in a query. Assuming that you have a collection called "products", to check if a specific product name already exists, please use the following lines of code:
val db = FirebaseFirestore.getInstance()
val productsRef = db.collection("products")
Query queryByProductName = productsRef.whereEqualTo("productName", newProductName)
queryByProductName.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
if (!task.result.isEmpty) {
Log.d(TAG, "$newProductName already exists.")
} else {
Log.d(TAG, "$newProductName doesn't exist.")
}
} else {
Log.d(TAG, "Error getting documents: ", task.exception)
}
}

Flow emitting value even when there are no change

I have a datastore in my android app where I am storing my profile details. and retrieving as follows
suspend fun saveUser(user: User) {
dataStore.edit {
it[USER_ID] = user.id
it[USER_NAME] = user.name
it[USER_MOBILE] = user.phone
it[USER_EMAIL] = user.email
it[USER_IMAGE] = user.image
it[USER_ADDRESS] = user.address
}
}
val userDate = dataStore.data
.catch { e ->
if (e is IOException) {
Log.e("PREFERENCE", "Error reading preferences", e)
emit(emptyPreferences())
} else {
throw e
}
}
.map { pref ->
val userId = pref[USER_ID] ?: ""
val userName = pref[USER_NAME] ?: ""
val userEmail = pref[USER_EMAIL] ?: ""
val userImage = pref[USER_IMAGE] ?: ""
val userPhone = pref[USER_MOBILE] ?: ""
val userAddress = pref[USER_ADDRESS] ?: ""
User(
name = userName,
image = userImage,
address = userAddress,
phone = userPhone,
id = userId,
email = userEmail
)
}
Along with it I am saving the availibility status of the User
suspend fun saveIsAvailable(boolean: Boolean) {
dataStore.edit {
it[USER_IS_AVAILABLE] = boolean
}
}
I am collecting user profile details like this in my viewmodel
viewModelScope.launch(Default) {
RiderDataStore.userDate.collect {
user.postValue(it)
}
}
Whenever I change the User availibility my user details flow also gets triggered which is unneccessary and causes ui jittering (image reloads). Why does this happen and how to enable the flow to only trigger if the data changes specifically of the user detail.
This is because you update a user property (in DataStore) and at the same time with userDate.collect you're observing all changes made to the user (in DataStore). Your current code has no way to distinguish between "good" and "bad" updates of the user.
Since you seem to ignore the availability in your DataStore Flow called userDate, your returned User objects should indeed stay identical after availability changes. The default behavior for Kotlin Flow is to emit on every change, even if the data is identical. But you can fix that simply by adding a .distinctUntilChanged() after the map operator like:
val userDate = dataStore.data
.catch { e ->
if (e is IOException) {
Log.e("PREFERENCE", "Error reading preferences", e)
emit(emptyPreferences())
} else {
throw e
}
}
.map { pref ->
val userId = pref[USER_ID] ?: ""
val userName = pref[USER_NAME] ?: ""
val userEmail = pref[USER_EMAIL] ?: ""
val userImage = pref[USER_IMAGE] ?: ""
val userPhone = pref[USER_MOBILE] ?: ""
val userAddress = pref[USER_ADDRESS] ?: ""
User(
name = userName,
image = userImage,
address = userAddress,
phone = userPhone,
id = userId,
email = userEmail
)
}.distinctUntilChanged()
See also docs. It makes sure identical data is not emited over and over.

Need help solving Firebase query in Android Studio

I'm trying to see if this "employee" has a service where the serviceComplete field is false (in other words trying to see if this employee has an active service that is not complete) if a Toast message pops up letting the employee know he cannot accept more services has he has an active one. If not the employee should be able to accept the service.
My problem is no matter what I do this firebase query seems to think there are documents in my DB that do not exist. Every time I go to accept the service it displays the toast. Meaning there is a collection "services" where a document has the field "serviceCompleted" which is equal to "false" but in my DB there is no collection "services" under employees
My database showing no collection "services" exist underneath "employees"
and here is my Kotlin code
fun setButton(serviceID: String, eID: String){
val btnAcceptService = view.findViewById<Button>(R.id.btnAcceptService)
btnAcceptService.setOnClickListener {
val queryEmpServices = database.collection("employees").document(eID).collection("services").whereEqualTo("serviceComplete", false)
queryEmpServices.get().addOnSuccessListener { documents ->
if (documents != null){
Toast.makeText(applicationContext,"You already have a service active!", Toast.LENGTH_SHORT).show()
}else {
database.collection("services").document(serviceID).update("saccept", true).addOnSuccessListener {
database.collection("services").document(serviceID).get().addOnSuccessListener { document ->
if (document != null) {
val Location = document.get("ulocation").toString()
val serviceType = document.get("serviceType").toString()
val uComment = document.get("ucomment").toString()
val uID = document.get("uid").toString()
if (document.getBoolean("saccept") == true) {
database.collection("users").document(document.get("uid").toString()).collection("services").document(serviceID).update("saccept", true).addOnSuccessListener {
database.collection("employees").document(mAuth.currentUser!!.uid).get().addOnSuccessListener { document ->
if (document != null) {
val calendar = Calendar.getInstance()
val simpleDateFormat = SimpleDateFormat("dd-MM-yyyy HH:mm:ss")
val acceptDate = simpleDateFormat.format(calendar.time)
val eFullName = document.get("ename").toString() + " " + document.get("esurname").toString()
val eCompany = document.get("ecompany").toString()
database.collection("users").document(uID).get().addOnSuccessListener { document ->
val uName = document.get("name").toString()
val uPhonenumber = document.get("phonenumber").toString()
val serviceAccept = EmployeeServiceAccept(acceptDate, serviceID, Location, serviceType, uComment, uName, uPhonenumber, false)
database.collection("employees").document(mAuth.currentUser!!.uid).collection("acceptedservices").document(serviceID).set(serviceAccept)
database.collection("services").document(serviceID).update("acceptedby", eFullName + ", " + eCompany)
database.collection("users").document(uID).collection("services").document(serviceID).update("acceptedby", eFullName + ", " + eCompany)
Toast.makeText(applicationContext, "Service Accepted", Toast.LENGTH_SHORT).show()
}
}
}
}
}
} else {
Toast.makeText(applicationContext, "Failed to accept service", Toast.LENGTH_SHORT).show()
}
When you are using the Task.addOnSuccessListener(OnSuccessListener) method on a Query object, the result that you get can be a success or a failure, never both and never null. It will always be one, or the other.
That being said, you should never check the documents object against nullity because it can never be null. What you should do instead, is to check the "documents" object, which is of type QuerySnapshot to see if isEmpty() or not:
if (!documents.isEmpty){
Toast.makeText(applicationContext,"You already have a service active!", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(applicationContext,"You don't have a service active!", Toast.LENGTH_SHORT).show()
}
Where indeed the Toast message from the "else" part of the statement will be displayed, as there are no documents present in the QuerySnapshot object:
"You don't have a service active!"

OnCompleteListener for RealTimeDatabase is not called [duplicate]

This question already has answers here:
How to return a list from Firestore database as a result of a function in Kotlin?
(3 answers)
Closed 3 years ago.
I wonder why the onCompleteListener is not called but the data are saved to the FireBaseDatabase successfully.
I just want to say: if the data are saved to FireBaseDatabase, set isSuccedded to true, but this does not happen. isSucceeded is always false.
val isSaved = SaveUserToDatabase(username)
if (isSaved) {
Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show()
val intent = Intent(this, LatestTransactions::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.putExtra(CURRENT_USER, mAuth.currentUser.toString())
startActivity(intent)
} else {
register_tv_error.text = "Couldn't save user"
}
and the function:
private fun SaveUserToDatabase(username: String): Boolean {
val uid = mAuth.uid
var isSucceeded = false
val user = User(uid!!, username)
myRef = database.getReference("/users/$uid")
myRef.setValue(user).addOnCompleteListener{
if(it.isSuccessful){
isSucceeded = true
}
}
return isSucceeded
}
What is the Problem?
Please avoid returning data from method that runs asynchronously code.
Your logic can be like this.
private fun SaveUserToDatabase(username: String){
val uid = mAuth.uid
val user = User(uid!!, username)
myRef = database.getReference("/users/$uid")
myRef.setValue(user).addOnCompleteListener{
if(it.isSuccessful){
updateView(it.isSuccessful);
}
}
}
private fun updateView(isSuccess: Boolean){
if (isSuccess) {
Toast.makeText(this, "Success", Toast.LENGTH_SHORT).show()
val intent = Intent(this, LatestTransactions::class.java)
intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK.or(Intent.FLAG_ACTIVITY_CLEAR_TASK)
intent.putExtra(CURRENT_USER, mAuth.currentUser.toString())
startActivity(intent)
} else {
register_tv_error.text = "Couldn't save user"
}
For more info read asynchronous-vs-synchronous in this question

Categories

Resources