So, why can I call any pair of key-value from this one, but can't from this one?
This is my code
var firstKitList = mutableListOf<String>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_kit_list)
val mainKitList = kitListView
val mainListViewAdapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, firstKitList)
mainKitList.adapter = mainListViewAdapter
db1.collection("cities").get().addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
val data = document.data
val country = data["USA"] as String
firstKitList.add(country)
}
mainListViewAdapter.notifyDataSetChanged()
}
If I switch "cities" for "KitList" and val country = data["USA"] as String for val rope = data["skipping"] as String it works... Could anyone explain this to me please?
1st Answer:
In your firestore data I don't see data for key "USA" and "skipping". Are you sure that this data are correct?
Answer for 2nd question.
I suggest you to create data class with fields you want. Then, you can map your document data to your class with code:
document.data.toObject(DataClass::class.java)
Or, if you have more than one document in QuerySnapshot:
val dataList = mutableListOf<DataClass>()
querySnapshot.documents.mapTo(dataList) { it.toObject(DataClass::class.java)}
Basing on your code, you can do this:
querySnapshot.documents.mapTo(firstKitList) { it.toObject(DataClass::class.java)}
#Edit1
this is you data model:
class City(var cityId: String,
var state: String,
var name: String,
var country: String) {
//remember to add empty constructor.
constructor() : this("", "", "", "")
}
When you tap on list on this item, create an Intent with all this data, and start new activity.
#Edit2
If you want to add document with specific id:
FirebaseFirestore.getInstance().collection("collectionName")
.document("documentId, for example LA").set(city)
If you want to pass id to previous activity, learn something about startActivityForResult method :)
Related
My User object has a List<Post>, each Post has a List<Tag>.
Now I would like to add a Tag item to the List<Tag>.
User:
data class User(
val id: String,
val name: String,
val posts: List<Post> = listOf()
)
Post:
data class Post(
val id: Int,
val name: String
val tags: List<Tags> = listOf()
)
Now I would like to update my MutableStateFlow containing the User object:
private val _userStateFlow: MutableStateFlow<User?> = MutableStateFlow(User())
val userStateFlow: StateFlow<User?> = _userStateFlow
To find the correct Post (containing the List<Tag> I want to update) I have the id of it (passedPostId)
val postsList = userStateFlow.value?.posts!!.toMutableList()
val tagsList = postsList.find { it.id == passedPostId }?.tags?.toMutableList()
tagsList.add(Tag("test"))
Now I got an updated tagsList but I need an updated postsList to pass it to my MutableStateFlow.
I am not sure how to do it.
This is how I update my MutableStateFlow:
_userStateFlow.update { it?.copy(posts = newPosts)
If I have the index of the post I want to update everything works fine, but In my Case I only can call .find because all i have is the id of the post.
val posts = userStateFlow.value?.posts!!.toMutableList()
posts[index] = posts[index].copy(tags = posts[index].tags.plus(Tag("test")
userStateFlow.value = userStateFlow.value?.copy(posts = posts)
You can create a function to add a tag to your immutable Post object - may as well stick it inside the class:
data class Post(
val id: Int,
val name: String
val tags: List<Tag> = listOf()
) {
// copy this object, replacing the tag list with a copy that has the new one appended
fun addTag(newTag: Tag) = copy(tags = tags + newTag)
}
Then you can do:
// alternative to putting it in the User class - whatever feels better
fun User.addTag(postId: Int, tag: Tag) =
// copy this User, including copying its list of Posts
// but any with a matching ID get the tag added
copy(posts = posts.map { post ->
if (post.id == postId) post.addTag(tag) else post
})
and update with
userStateFlow.value = userStateFlow.value!!.addTag(passedPostId, Tag("test"))
Try it here if you like
I fixed it by simply getting the index of the Post so I could use the code that worked before:
val postPosition = postsList.indexOfFirst {
it.id == passedPostId
}
postsList[postPosition] = postsList[postPosition].copy(tags = tagsList)
_userStateFlow.update { it?.copy(posts = postsList)
I feel pretty stupid to not have thought of that in the first place.
I would like to ask for help on how to retrieve data from Firestore for nested Array of Maps called "cities" into MutableList , which I then want to insert into recycler view, where the data from the “regions” are for the header and data “cities” for the regular list items.
Data for regions: MutableList , when I follow the procedure https://medium.com/firebase-tips-tricks/how-to-map-an-array-of-objects-from-cloud-firestore-to-a-list -of-objects-122e579eae10 by Alex Mamo, got fine, but data for: cities: MutableList , according same approach, is null (unable to retrive).
Can you please advise how to get data for “cities”?
P.s. somewhere I read the recommendation to iterate over "cities", but I have no idea how, please go straight for an example (ideally in Kontlin).
Code:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
…..
regionsRef.get().addOnCompleteListener { document ->
if (document.isSuccessful()) {
val documentSnapshot = document.result
// Retrieve array of maps for „regions“
val regions = documentSnapshot.toObject(RegionDocument::class.java)?.regions
// Retrieve array of maps for „cities“
val cities = documentSnapshot.toObject(CityDocument::class.java)?.cities
…
}
}
Data classes for object City:
data class City(
val cityNumber: Long? = null,
val cityName: String? = "" )
data class CityDocument(
var cities: MutableList<City>? = null)
Firestore structure:
To be able to get the data that corresponds to your document structure, you need three classes:
class Document {
var regions: MutableList<Region>? = null
}
class Region {
var cities: MutableList<City>? = null
var regionName: String? = null
var regionNumber: Long? = null
}
class City {
var cityName: String? = null
var cityNumber: Long? = null
}
And below you can find a solution for reading all cities:
val db = FirebaseFirestore.getInstance()
val docIdRef = db.collection("collName").document("docId")
docIdRef.get().addOnCompleteListener { task ->
if (task.isSuccessful) {
val document = task.result
if (document != null) {
val doc = document.toObject(Document::class.java)
if (doc != null) {
val regions = doc.regions
if (regions != null) {
for (region in regions) {
val cities = region.cities
//Do what you need to to do with your List<City>.
}
}
}
}
} else {
Log.d("TAG", task.exception!!.message!!) //Never ignore potential errors!
}
}
Now, simply replace collName and docId with the one you have in your database.
I have this type of array in firebase but how to fetch it and use in kotlin
I was able to get as String but how to get its as a data class Like this
data class Comment(
val uid: String,
val comment: String,
val stamp: Timestamp
)
and here's the code of getting string
var text by remember { mutableStateOf("loading...") }
FirebaseFirestore.getInstance().collection("MyApp")
.document("Something").get().addOnSuccessListener {
text = it.get("Comments").toString()
}
Firebase has a toObject method that can be used to turn your document into a custom object.
db.collection("Comments")
.get()
.addOnSuccessListener { documents ->
for (document in documents) {
val comment = document.toObject<Comment>()
}
}
The Comment data class should also define default values. So, it should be like...
data class Comment(
val uid: String = "",
val comment: String = "",
#ServerTimeStamp val stamp: Date? = null
)
I got ArrayLists with HashMaps represents my entities entities just using this:
val cleanAnswers: List<Answer> = (document.get(FIELD_ANSWERS)
as ArrayList<HashMap<String, Any>>).map {
Answer(
it[FIELD_VARIANT] as String,
it[FIELD_IS_CORRECT] as Boolean
)
}
My entity:
class Answer(val answer: String,
val isCorrect: Boolean) : Serializable
calculateButton.setOnClickListener {
val panelC = binding.panelCount.text.toString()
val panelS = binding.panelSize.text.toString()
val landS = binding.landSlope.text.toString()
val landSi = binding.landSize.text.toString()
val cit = binding.city.text.toString()
val sun = (1000).toInt()
val air = (1.25).toFloat()
val cel = (25).toInt()
val verim = ((sun * air)/ cel).toString().toDouble()
if (panelC.equals("") || panelS.equals("")|| landS.equals("")|| landSi.equals("")||cit.equals("")){
Toast.makeText(requireContext(),"Alanları Doldurunuz.", Toast.LENGTH_SHORT).show()
}
else{
val action = SignUpCalculateFragmentDirections.actionSignUpCalculateFragmentToDataFragment()
Navigation.findNavController(view).navigate(action)
}
val postMap = hashMapOf<String, Any>()
postMap.put("Panel Sayisi",binding.panelCount.text.toString())
postMap.put("Panel Boyutu",binding.panelSize.text.toString())
postMap.put("Arazi Eğimi",binding.landSlope.text.toString())
postMap.put("Arazi Boyutu",binding.landSize.text.toString())
postMap.put("Şehir",binding.city.text.toString())
postMap.put("date", Timestamp.now())
postMap.put("verim",verim.toString().toDouble())
firestore.collection("Posts").add(postMap).addOnFailureListener {
}.addOnFailureListener {
Toast.makeText(requireContext(),it.localizedMessage,Toast.LENGTH_SHORT).show()
}
There is a calculatePage Fragment Codes. On this page, I am trying to make a yield calculation based on the data I receive from the user. However, I need to add the values that are kept constant in the efficiency calculation, such as "sun", "cel", "air" that I defined in the code. I wrote a random operation there as an example. To see if I can write inside the text I'm trying to print without getting any errors. But the app crashed.
private fun getData(){
firestore.collection("Posts").addSnapshotListener { value, error ->
if (error!=null){
Toast.makeText(requireContext(),error.localizedMessage,Toast.LENGTH_SHORT).show()
}else{
if (value !=null){
if (!value.isEmpty){
val documents = value.documents
for (document in documents){
val pc = document.get("Panel Sayisi") as String
val ps = document.get("Panel Boyutu") as String
val ls = document.get("Arazi Eğimi") as String
val lsi = document.get("Arazi Boyutu") as String
val c = document.get("Şehir") as String
val v = document.get("verim") as Double
val post = Post(pc,ps,ls,lsi,c,v)
postArrayList.add(post)
}
}
}
}
val db = FirebaseFirestore.getInstance()
db.collection("Posts").orderBy("date",Query.Direction.DESCENDING).limit(1)
.get()
.addOnCompleteListener {
val result : StringBuffer = StringBuffer()
if (it.isSuccessful){
for (document in it.result){
result.append(document.data.getValue("verim"))
}
verimText.setText(result)
}
}
On this page, I added the values I defined in my class named 'post' to the postList and added them to Firestore.
data class Post(val pc: String, val ps: String, val ls: String, val lsi: String, val c: String, val v : Double)
#ServerTimestamp
var date: Date? = null
This is my post class
The error is like this: java.lang.NullPointerException: null cannot be cast to non-null
type kotlin.Double
As I explained at the beginning of my question, what I am trying to do is using both the data such as "panelCount", "panelSize" that I get from the user and the "sun", "cel", "air" values that are defined as constants, using the "verimText.setText(result)" in the DataFragment page. I need to show this calculation to the user.
The user enters values such as 'Panel Sayisi', 'Panel Boyutu' that should be used while calculating on the calculation page. I need to show this to the user in verimText using both this data and the 'cel', 'sun', 'air' constants that I wrote in the first code.
PS: verim: 20000 value is the result of hypothetical values that I wrote in the first code. In this part, I need to make a calculation using the other data entered by the user and these constant values and show it in the verimText.
class MyObj {
lateinit var id: String
lateinit var name: String
}
var listA : List<MyObj> = [id=1 name=a] [id=2 name=b] [id=3 name=c]
var llistB : List<MyObj> = [id=2 name=b] [id=3 name=c] [id=4 name=d]
I want get the result listC
as below
listC = listA union listB = [id=1 name=a][id=2 name=b][id=3 name=c][id=4 name=d]
data class MyObj (var id: String, var name: String)
val listA = listOf(MyObj(id="1",name="a"), MyObj(id="2",name="b"), MyObj(id="3",name="c"))
val listB = listOf(MyObj(id="2",name="b"), MyObj(id="3",name="c"), MyObj(id="4",name="d"))
val set = mutableSetOf<MyObj>()
set.addAll(listA)
set.addAll(listB)
val listC = set.toList()
This does what you asked but you should need how to write valid Kotlin first. Please take a look at the tutorial.
Also, if you do not want a repeating ID, you should use a map instead of list.