Kotlin - Compare two lists with startsWith() - android

I'd like a better way to loop through the keyList and if a key starts with the one of the comparator to grab that comparator string and add it to a Map as a header like so MutableMap>..the list being all the key items that match the comparator..
keysList: List<String>
val comparators = listOf("error", "customer", "custom", "feature")
So far I am doing it this way
private fun addToMap(key: String, attributeMap: MutableMap<String, MutableList<String>>) {
val list: MutableList<String> = attributeMap[getHeader(key)] ?: mutableListOf()
list.add(key)
attributeMap[getHeader(key)] = list
}
private fun getHeader(key: String): String {
val compareMap = mapOf("error" to "Error Attributes", "customer" to "Customer Attributes",
"custom" to "Customer Attributes", "feature" to "Feature Attributes", "request.header" to "Request Header Attributes",
"request.parameter" to "Request Parameter Attributes", "request" to "Other Request Attributes")
val defaultKeys = listOf("error.expected", "error.class", "error.message", "host", "httpResponseCode", "transactionName", "transactionUiName") // contains
for ((k, v) in compareMap) {
return if (key.startsWith(k)) {
v
} else if (key in defaultKeys) {
"Error Attributes"
} else {
"Custom Attributes"
}
}
return "Custom Attributes"
}

You could use the .any function like this:
if (comparators.any { key.startsWith(it) })
// add to map

Related

Send Data From Firestore To Shared Preferences Kotlin in Android Studio

I'm building an app and I have Firestore server, when specific button pressed I wanted to get data from firebase and save it to the Shared Preference but it's no seem to work.
db.collection("Groups1").document("${codeEntered}").get().addOnSuccessListener {doc ->
if(doc.exists()){
val groups = sharedPreferences.getStringSet("groupCodes", HashSet<String>())
if(groups?.contains(codeEntered)!!){
Toast.makeText(getApplicationContext(), "You're Already in ${codeEntered}.", Toast.LENGTH_LONG).show()
}else {
Toast.makeText(getApplicationContext(), "You Enter ${codeEntered} Group!", Toast.LENGTH_LONG).show()
groups?.plusAssign(codeEntered)
editSharedPreferences.putStringSet("groupCodes", groups)
val data = doc.data.hashCode()
//Add Set The Group In FireStore
db.collection("Groups1").document(codeEntered).get().addOnSuccessListener {doc ->
var data = doc.getData() as MutableMap<String, Any>
data["${sharedPreferences.getInt("mainId", 0)}"] = 0
db.collection("Groups1").document(codeEntered).set(data)
editSharedPreferences.apply()
}
getData(object : MyCallback {
override fun onCallback(value: HashMap<String, Int>) {
listView.adapter = adapterListView(MainActivity.appContext, value, sizeOfListMain, sharedPreferences.getInt("mainColor", 0)) }
})
var codeGroup = sharedPreferences.getStringSet("groupCodes", HashSet<String>()) as HashSet<String>
}
}else{
Toast.makeText(getApplicationContext(), "${codeEntered} is Not Exists.", Toast.LENGTH_LONG).show()
}
}

How to parse nested JSON object with Retrofit/Moshi

I used this CodeLabs tutorial to learn how to make an HTTP request from the Google Books API
https://codelabs.developers.google.com/codelabs/kotlin-android-training-internet-data/#4
Right now, I'm trying to access a nested JSON object that the Google Books API spits out
I.e
"items": [{
"kind": "books#volume",
"id": "mOsbHQAACAAJ",
"volumeInfo" : {
"description": "Young wizard Harry Potter finds himself back at the miserable Hogwarts School of Witchcraft and Wizardry. He doesn't realize the difficulty of the task that awaits him. Harry must pull out all the stops in order to find his missing friend. No Canadian Rights for the Harry Potter Series HARRY POTTER and all related characters and elements are trademarks of and (c) Warner Bros. Entertainment Inc. Harry Potter publishing rights (c) J. K. Rowling. (s05)",
"imageLinks": {
"smallThumbnail": "http://books.google.com/books/content?id=mOsbHQAACAAJ&printsec=frontcover&img=1&zoom=5&source=gbs_api",
"thumbnail": "http://books.google.com/books/content?id=mOsbHQAACAAJ&printsec=frontcover&img=1&zoom=1&source=gbs_api"
}
},
I just want the description and thumbnail property.
My interface for the API service is
package com.example.customapp.network
import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Call
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET
//Code from https://codelabs.developers.google.com/codelabs/kotlin-android-training-internet-data/#3
private const val BASE_URL = "https://www.googleapis.com/books/v1/"
private val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
private val retrofit = Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create(moshi))
.baseUrl(BASE_URL)
.build()
interface BookApiService {
//Get annotation specifies the endpoint for this web service method.
//when getProperties() method is invoked, Retrofit appends the endpoint 'book' to the base URL
//And creates a Call object. The Call object is used to start the request.
#GET("volumes?q='harry+potter")
suspend fun getProperties(): BookProperty
}
object BookApi {
val retrofitService: BookApiService by lazy {
retrofit.create(BookApiService::class.java)
}
}
}
My BookProperty.kt is
data class BookProperty(#field:Json(name = "items" ) val bookDetail: List<BookDetail>)
data class BookDetail(#field:Json(name = "volumeInfo") val volumeInfo: VolumeInfo)
data class VolumeInfo(#field:Json(name = "description") val description: String, #field:Json(name= "imageLinks") val imageLink: ImageLink)
data class ImageLink(#field:Json(name = "thumbnail") val thumbnail: String)
I'm calling the API from my ViewModel
val readAllData: LiveData<List<BookItem>>
private val repository: BookRepository
private val _response = MutableLiveData<String>()
val response: LiveData<String>
get() = _response
init {
val bookDao = BookDatabase.getDatabase(application).bookDao()
repository = BookRepository(bookDao)
readAllData = repository.readAllData
}
fun addBook(book: BookItem) {
viewModelScope.launch(Dispatchers.IO) {
repository.addBook(book)
}
}
fun updateBook(book: BookItem) {
viewModelScope.launch(Dispatchers.IO) {
repository.updateBook(book)
}
}
fun getBookDetailProperties() {
viewModelScope.launch {
try {
//calling get properties from the BookApi service creates and starts the network call
//on a background thread
var listResult = BookApi.retrofitService.getProperties()
_response.value = "${
listResult.bookDetail[0].volumeInfo.description} book properties received"
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
}
}
I'm trying to make an HTTP request each time I update an item on my CRUD app i.e when I click a button, but I can't seem to get any response back. This is my UpdateFragment where I initiate the API call.
class UpdateFragment : Fragment() {
//Read up on delegation
//https://codelabs.developers.google.com/codelabs/kotlin-bootcamp-classes/#7
//UpdateFragmentArgs is a class that is automatically generated
//when we created an argument for our Update Fragment in the nav graph
//UpdateFragmentArgs will contain our current book
//we can also use bundle
private val args by navArgs<UpdateFragmentArgs>()
private lateinit var mBookViewModel: BookViewModel
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.fragment_update, container, false)
//So the keyboard doesn't push the EditText fields up
this.activity?.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)
Glide
.with(this)
.load(args.currentBook.image)
.into(view.bookImageDetail)
mBookViewModel = ViewModelProvider(this).get(BookViewModel::class.java)
view.apply {
updateInputName.setText(args.currentBook.title)
updateInputAuthor.setText(args.currentBook.author)
updateBookDesc.text = args.currentBook.desc
updateRatingBar.rating = args.currentBook.rating.toFloat()
updateBookCompleted.isChecked = args.currentBook.finished
updateBookCompleted.text =
if (updateBookCompleted.isChecked) getString(R.string.book_completed) else getString(
R.string.book_not_completed
)
updateDateCreated.text = getString(R.string.date_created, args.currentBook.dateCreated)
}
view.updateBtn.setOnClickListener {
updateItem()
}
view.updateBookCompleted.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
view.updateBookCompleted.text = getString(R.string.book_completed)
} else {
view.updateBookCompleted.text = getString(R.string.book_not_completed)
}
}
return view
}
private fun updateItem() {
val bookName = updateInputName.text.toString()
val bookAuthor = updateInputAuthor.text.toString()
val bookRating = updateRatingBar.rating.toDouble()
val bookFinished = updateBookCompleted.isChecked
if (inputCheck(bookName, bookAuthor)) {
//***Initiate API call here ****
mBookViewModel.getBookDetailProperties()
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
})
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
}
}
private fun inputCheck(bookName: String, authorName: String): Boolean {
return !(TextUtils.isEmpty(bookName) && TextUtils.isEmpty(authorName))
}
}
The issue is I can't get any response from the API call - I'm not sure if it's because of the nested objects in the JSON. Please help me shed some light on this, I'm still new to Kotlin programming.
I found out the reason why I was not getting any response.
In my UpdateFragment, I'm doing this:
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
})
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
I am navigating back to another fragment before I can observe any changes from the HTTP response. This causes the observer to stop observing any changes, and thus I can't get a response. I just need to put my code inside the callback, so I can do something with the data I received. Like so:
//Get description and image from API
mBookViewModel.response.observe(viewLifecycleOwner, {
println("Get resp " + it)
//Create book object
val updatedBook = BookItem(
args.currentBook.id,
bookName,
bookAuthor,
args.currentBook.desc,
args.currentBook.image,
bookRating,
args.currentBook.dateCreated,
bookFinished
)
//update current book
mBookViewModel.updateBook(updatedBook)
Toast.makeText(requireContext(), "Updated book successfully!", Toast.LENGTH_SHORT)
.show()
//navigate back
findNavController().navigate(R.id.action_updateFragment_to_listFragment)
})
Hopefully this helps out anyone who has just started out learning LiveData and using HTTP requests.

How to retrieve a child from Firebase when there is a unique key Kotlin

I want to retrieve specific child values like (phonenumber, firstname, familyname) from Firebase real time database
but there is a unique key for each user
and this is the tree:
I've tried this:
var loginRef = rootRef.child("users").orderByChild("phoneNumber").equalTo(phone).addListenerForSingleValueEvent(
object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// Get data object and use the values to update the UI
val phoneNumber = dataSnapshot.getValue<User>()!!.phoneNumber
// ...
Toast.makeText(applicationContext, "phone number is: $phoneNumber", Toast.LENGTH_LONG).show()
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Data failed, log a message
Log.w(TAG, "LoginData:onCancelled", databaseError.toException())
// ...
Toast.makeText(applicationContext, "error", Toast.LENGTH_LONG).show()
}
})
and I have a simple model called User to handle the data (I know the passwords should be hashed here)
#IgnoreExtraProperties
data class User(
var firstName: String? = "",
var fatherName: String? = "",
var familyName: String? = "",
var phoneNumber: String? = "",
var password: String? = ""
) {
#Exclude
fun toMap(): Map<String, Any?> {
return mapOf(
"firstName" to firstName,
"fatherName" to fatherName,
"familyName" to familyName,
"phoneNumber" to phoneNumber,
"password" to password
)
}
}
but dataSnapshot.getValue<User>()!!.phoneNumber will never work, since the first node retrieved in this query is the unique key
what I need is something like dataSnapshot.child("unique-key/phoneNumber").value for each child i want to use, but a way easier and more efficient than making .addChildEventListener for each node
Let's firstly give some notes one the code:
first thing you need to be aware of is here:
dataSnapshot.getValue<User>()!!.phoneNumber
as it might be null if phoneNumber doesn't exist and will throw an error.
secondly, assuming you made some null handling it will still retrieve you empty string, because what you sent to model is just the unique key, and of course you can't handle it with this model.
The easiest way to solve this and get the children of retrieved node is by using for loop according to this solution: https://stackoverflow.com/a/38652274/10324295
you need to make for loop puts each item into an array list, try this code:
val userList: MutableList<User?> = ArrayList()
var loginRef = rootRef.child("users").orderByChild("phoneNumber").equalTo(phone).addListenerForSingleValueEvent(
object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
userList.clear()
for (userSnapshot in dataSnapshot.children) {
val user: User? = userSnapshot.getValue(User::class.java)
userList.add(user)
// Get Data object and use the values to update the UI
// ...
Toast.makeText(applicationContext, "hi: ${user!!.phoneNumber}", Toast.LENGTH_LONG).show()
}
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Data failed, log a message
Log.w(TAG, "LoginData:onCancelled", databaseError.toException())
// ...
Toast.makeText(applicationContext, "error", Toast.LENGTH_LONG).show()
}
})
var loginRef = rootRef.child("users").orderByChild("phoneNumber").equalTo(phone).addListenerForSingleValueEvent(
object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
// retreive all children firstly by foreach
dataSnapshot.children.forEach { data ->
val userModel = data.getValue(User::class.java)
val phoneNumber = userModel!!.phoneNumber
Toast.makeText(applicationContext, "phone number is: $phoneNumber",
Toast.LENGTH_LONG).show()
}
// ...
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Data failed, log a message
Log.w(TAG, "LoginData:onCancelled", databaseError.toException())
// ...
Toast.makeText(applicationContext, "error",
Toast.LENGTH_LONG).show()
}
})

How to read child of child data from firebase in android / kotlin

I am trying to read data from my firebase realtime database to a TextView. But every time it shows null in TextView. I want to read single user data. I want to read data when user give input.
My Data Structure look like this..
{
"Date": {
"04-10-2019": {
"-LqR-e2UJLJCccqfgGi1":{
address: Dhaka
date: 04-10-2019
name: Mark
phoneNo: 017#######
serialNo: -LqR-e2UJLJCccqfgGi1
type: New
},
},
"05-10-2019": {
"-LqU-e2UJLJCDcqfgGi9":{
address: Dhaka
date: 04-10-2019
name: Tony
phoneNo: 017#######
serialNo: -LqU-e2UJLJCDcqfgGi9
type: OLd
},
}
}
The code that I am trying.
class Info(
val serialNo: String? = "",
val name: String = "",
val address: String = "",
val phoneNo: String = "",
val date: String = "",
var type: String = ""
)
private fun saveInfo() {
// Edit Text Field
val name = editTextName.text.toString().trim()
if (name.isEmpty()) {
editTextName.error = "Please Enter Your Name"
return
}
val address = editTextAddress.text.toString().trim()
if (address.isEmpty()) {
editTextAddress.error = "Please Enter Your Address"
return
}
val phoneNo = editTextPhoneNo.text.toString().trim()
if (phoneNo.isEmpty()) {
editTextPhoneNo.error = "Please Enter Your Phone Number"
return
}
val date = dateText.text.toString().trim()
//Radio Button
var type = ""
when {
checkboxNew.isChecked -> type += "New"
checkboxOld.isChecked -> type += "Old"
radioGroup.checkedRadioButtonId <= 0 -> {
Toast.makeText(applicationContext, "Please Check New or Old", Toast.LENGTH_LONG)
.show()
return
}
}
val current = LocalDateTime.now()
val formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy")
val formatted = current.format(formatter)
val myRef = FirebaseDatabase.getInstance().getReference("Date").child(formatted)
myRef.orderByChild(formatted).startAt("28-09-2019").endAt("31-12-2070")
val patientId = myRef.push().key
val patient = Info(patientId, name, address, phoneNo, date, type)
myRef.child((patientId).toString()).setValue(patient).addOnCompleteListener {
Toast.makeText(applicationContext, "Info saved Successfully", Toast.LENGTH_LONG).show()
myRef.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (data in dataSnapshot.children) {
val user = data.child(formatted).value.toString()
dataText.text = user
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
}
I want to show the data into a textview when the user give input and hit a button.
If you are trying to read the date attribute from the database then change this :
val user = data.child(formatted).value.toString()
Into this:
val user = data.child("date").value.toString()
Inside the child() you need to pass the name of the attribute.

Decide to create a new item or update an old one in firebase

I have two methods, in one I create a new list item, in the second I update the current list item.
private fun addTarget() {
val name = nameEditText?.text.toString().trim()
val description = descriptionEditText?.text.toString().trim()
if (!TextUtils.isEmpty(name)) {
val id: String = databaseReference?.push()?.key.toString()
val target = Target(guid = id, name = name, description = description)
databaseReference?.child(id)?.setValue(target)
} else Log.d("some", "Enter a name")
}
private fun updateTarget() {
val name = nameEditText?.text.toString().trim()
val description = descriptionEditText?.text.toString().trim()
val map = mapOf("name" to name, "description" to description)
databaseReference?.child(arguments?.getString(KEY_TARGET_GUID, "") ?: "")?.updateChildren(map)
}
I need to clearly separate these two concepts, so there is a problem in the condition.
button?.setOnClickListener { if (condition?????) addTarget() else updateTarget() }
For example, in the Realm there is a method copyToRealmOrUpdate which looks for the field and if it finds it updates it if not then creates a new note. How can I do something like this in firebase?
I resolved my problem next:
When I go to the fragment I pass the guid from the list of all elements and if it is empty then I add if not then update.
button?.setOnClickListener {
if (arguments?.getString(KEY_TARGET_GUID, "").isNullOrEmpty()) addTarget()
else updateTarget()
}
I don't know how this is a good solution.
This is possible in Firebase if you are using exist() method like in the following lines of code:
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
//Do the update
} else {
//Do the addition
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
databaseReference.child("-LaVYDBpwiIcwhe9qz2H").addListenerForSingleValueEvent(valueEventListener)

Categories

Resources