How to get current count in for loop using firebase? - android

I am using Firebase FireStore to retrive my all collections data
but i want to print Log of each 5 interwal how can i get the current count of the loop?
If any suggestion to change loop please also share your solution with code..
Here is my code
db
.collection("news")
.orderBy("timestamp", Query.Direction.DESCENDING)
.whereLessThan("timestamp",tm)
.limit(10)
.get()
.addOnSuccessListener {
for (doc in it) {
val imgUrl = doc.getString("imageUrl")
val heading = doc.getString("headline")
val timestamp = doc.getTimestamp("timestamp")
val tagline = doc.getString("tagline")
val type = doc.getString("type")
// position%5 but how to get the current position?
}
}

for ((i, doc) in it.withIndex()) {
val imgUrl = doc.getString("imageUrl")
val heading = doc.getString("headline")
val timestamp = doc.getTimestamp("timestamp")
val tagline = doc.getString("tagline")
val type = doc.getString("type")
if(i%5==0) {
//add ur item
}

The QuerySnapshot and its Iterator don't expose any index information as you iterate. You would have to keep track of it yourself:
int i = 0
for (doc in it) {
if (++i % 5 == 0) {
// multiple of 5
}
}
Or you can iterate a List of snapshots the you get from getDocuments(), using its ListIterator, which will expose indexes.

Related

FireStore Documents only showing sometimes

Fetching data from my FireStore DB is unreliable and it worsens after adding a new feature that uses the first date fetch to do some more data fetchs
I've my user songs (which are document ids to match the data)
To
//To get user data
suspend fun getAllUserPlaylists():List<Playlists>
= try{ FS_USER.get().await().toObjects(Playlists::class.java) }
catch (e:Exception){ emptyList() /*getAllUserPlaylists()*/ }
//Function to call above funcion
val allUserPlaylist = GlobalScope.launch { vm.allUserPlaylists. postValue(MusicDB(). getAllUserPlaylists()) }
runBlocking {
//wait for data fetch to finish
allUserPlaylist.join()
Log.i("WORKS","${vm.allUserPlaylists.value}")
//Match ids with song data
vm.allUserPlaylists.value?.forEach {
val q = arrayListOf<Song>()
it.songsIDs.forEach { it2 ->
val t = FS_ALL_SONGS.document(it2).get().await().toObject(Song::class.java)
if (t != null) {
q.add(t)
}
}
//Insert
val vmT = vm.allUserPlaylists.value!!
vmT[vmT.indexOf(it)].songs = q
vm.allUserPlaylists.postValue(vmT)
}
}
data class Song(val mediaID: String = "", val title: String = "",
val artist: String = "", val songURL: String = "",
val imgURL: String = "", val album: String = "")
data class Playlists(val name:String= "", val mainArtists: String = "",
var songs:ArrayList<Song> = arrayListOf(),
val songsIDs:ArrayList<String> = arrayListOf(),
val imgURL:String = "",var pinned:Boolean = false)
What i dont get why is it so unreliable.In theory it should work but only does (10% of the time) after clearing all app data and cache.I simpliy add documents to a list with using their doc ids and display it using a recycler view that updates on value change.
So any sort of help is appreicated
Edited : My code doesn't provide any visible errors (to me) however even though getAllUsersPlaylists function returns valid results(doc IDs) which I've confirmed by logging.
Code snippet to match doc IDs with song data doesn't give expected results(also checked by logging).It doesn't match the Doc Ids with data so doesn't return song data that I've referred using its doc IDs.
And this data is later shown to the user using a recycler view which auto updates ui
Song IDs refer to the doc IDs for song data

How to retrieve data from nested array of maps in Firestore (Kotlin) for RecyclerView

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.

What should I do to print only the last data entered in Text?

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 post = Post(pc,ps,ls,lsi,c)
postArrayList.add(post)
}
}
}
}
val db = FirebaseFirestore.getInstance()
db.collection("Posts").orderBy("Panel Sayisi",Query.Direction.DESCENDING).limit(1)
.get()
.addOnCompleteListener {
val result : StringBuffer = StringBuffer()
if (it.isSuccessful){
for (document in it.result){
result.append(document.data.getValue("Panel Sayisi"))
}
verimText.setText(result)
}
}
}
}
It shows all the data I have added to Cloud Firestore that has the keyword "Panel Sayisi" in verimText. I only want the last data entered to be displayed.
Here is the problem. The last file I added is added in the middle. That's why the value I call last or the first call doesn't change. So it always calls the same value. This middle value should be written at the end or at the beginning. So I can get this data by calling ASCENDING or DESCENDING method.
When you are using the following call to:
.orderBy("Panel Sayisi",Query.Direction.DESCENDING)
It means that are only ordering the documents according to the "Panel Sayisi" field, which doesn't actually solve the problem. The problem is that you don't know that the "c4Q5...q4vP" document is the last document that was added. To solve this issue, you have to add an additional field of type Firestore Timestamp, as explained in my answer from the following post:
How to add Firebase Timestamp when adding an object of a data class to the Firestore in Kotlin?
And then use the following query:
val db = FirebaseFirestore.getInstance()
val query = db.collection("Posts").orderBy("timestamp", Query.Direction.DESCENDING).limit(1)

I want to show a data whose values I have determined myself in text

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.

Firestore startAt skips the snapshot given to it and behaves as startAfter instead

I have a PagingSource that pages through a firestore collection to return documents.
class ClipPageDataSource(mParams:Bundle, private val mAds:Boolean):PagingSource<QuerySnapshot, Clip>(), ClipDataSource {
var query : Query?= null
private val mFirestore = FirebaseFirestore.getInstance()
private var mBaseQuery = mFirestore.collection(SharedConstants.COLLECTION_CLIPS)
.orderBy("createdAt",Query.Direction.DESCENDING)
private var mLikedQuery = mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_USER_LIKES)
.orderBy("createdAt",Query.Direction.DESCENDING)
private var mSavedQuery = mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_SAVES)
.orderBy("createdAt",Query.Direction.DESCENDING)
val mine = mParams.getBoolean(ClipDataSource.PARAM_MINE)
val liked = mParams.getBoolean(ClipDataSource.PARAM_LIKED)
val saved = mParams.getBoolean(ClipDataSource.PARAM_SAVED)
val user = mParams.getString(ClipDataSource.PARAM_USER)
val first = mParams.getString(ClipDataSource.PARAM_FIRST)
val private = mParams.getBoolean(ClipDataSource.PARAM_PRIVATE)
override fun getRefreshKey(state: PagingState<QuerySnapshot, Clip>): QuerySnapshot? {
return null
}
override suspend fun load(params: LoadParams<QuerySnapshot>): LoadResult<QuerySnapshot, Clip> {
try {
query = when {
liked -> {
mLikedQuery
}
saved -> {
mSavedQuery
}
else -> {
mBaseQuery
}
}
if(mine){
query = query!!.whereEqualTo("createdBy.uid",Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
}else if(user!=null){
query = query!!.whereEqualTo("createdBy.uid",user)
}
query = if (private){
query!!.whereEqualTo("private",true)
}else{
query!!.whereEqualTo("private",false)
}
first?.let {
val item = mFirestore.collection(SharedConstants.COLLECTION_CLIPS).document(it).get().await()
if (item!=null){
query = query!!.startAt(item)
}
Log.d(TAG,"the first item fetched is ${item.data!!["id"]}")
}
query = query!!.limit(15)
val currentPage = params.key ?: query!!.get().await()
if (currentPage.size() < 1)
return LoadResult.Page(emptyList(),null,null)
val lastDocumentSnapshot = currentPage.documents[currentPage.size() - 1]
val nextPage = query!!.startAfter(lastDocumentSnapshot).get().await()
val clips = currentPage.map {
it.toObject(Clip::class.java)
}
return LoadResult.Page(clips,null,nextPage)
}catch (e:Exception){
return LoadResult.Error(e)
}
}
companion object{
private const val TAG = "DataSource"
}
}
So, in the above code, I have three different collections to fetch data from, and the required one is selected based on the parameters passed.
Now, when I fetch data using the mBaseQuery, and passing an id in first parameter, it returns the data correctly.
But, when I fetch data using the mLikedQuery or the mSavedQuery, instead of returning data from the id passed in first parameter, it uses the next item as the first one. Basically, startAt works as startAfter.
I have checked the snapshot fetched using the id passed in first is correct. So, the block in first?.let, works correctly. But, when the final query is executed, it skips the first item passed in startAt and instead starts from the next item in list.
This only happens with mLikedQuery and mSavedQuery and not with mBaseQuery.
Anybody got any idea what's happening here?
The DocumentReference you are providing to startAt is always for a document from the collection SharedConstants.COLLECTION_CLIPS:
val item = mFirestore.collection(SharedConstants.COLLECTION_CLIPS).document(it).get().await()
This works fine for your mBaseQuery because that query is querying the documents in the SharedConstants.COLLECTION_CLIPS collection, however your mLikedQuery and mSavedQuery are querying documents from different collections so providing a DocumentReference from the SharedConstants.COLLECTION_CLIPS collection as the startAt value here doesn't make sense, the query can't start at a document that doesn't exist in the collection you're querying.
Perhaps you need to set the item you provide to startAt based on which query is being used, e.g.:
...
first?.let {
val item = when {
liked -> {
mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_USER_LIKES).document(it).get().await()
}
saved -> {
mFirestore.collection(SharedConstants.COLLECTION_USERS)
.document(Prefs.getString(SharedConstants.PREF_SERVER_USER_ID,Firebase().getCurrentUserId()))
.collection(SharedConstants.SUB_COLLECTION_SAVES).document(it).get().await()
}
else -> {
mFirestore.collection(SharedConstants.COLLECTION_CLIPS).document(it).get().await()
}
}
...

Categories

Resources