How to sum of values from Firebase Database in Kotlin? - firebase

I have a Firebase Database with several purchases in Android app, points value is assigned for each purchase, then entries are showing by following way:
When userId is log in, a button appears and when user click on it the "showInfoClient" function is called and it must show user and the amount of points. Code is:
private val database = Firebase.database
private val myref = database.getReference("compras")
fun showInfoClient(view: View) {
val userlog = FirebaseAuth.getInstance().currentUser?.displayName
val alertDialogInfo = AlertDialog.Builder(this).create()
myref.orderByChild("userId").equalTo(userlog).addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
var sum = 0
for (data in dataSnapshot.children) {
sum += data.child("puntos").getValue(Int::class.java)!!
Toast.makeText(this#Principal, sum, Toast.LENGTH_SHORT).show()
}
}
override fun onCancelled(databaseError: DatabaseError) {}
})
alertDialogInfo.setTitle(userlog)
alertDialogInfo.setMessage("Puntos: ") // + sum
alertDialogInfo.setButton(
AlertDialog.BUTTON_POSITIVE, "OK"
) { dialog, _ ->; dialog.dismiss() }
alertDialogInfo.show()
val btnPositive = alertDialogInfo.getButton(AlertDialog.BUTTON_POSITIVE)
val layoutParams = btnPositive.layoutParams as LinearLayout.LayoutParams
layoutParams.weight = 100f
btnPositive.layoutParams = layoutParams
}
I have tried to use different options but i´m not able to set "sum" value on
alertDialogInfo.setMessage("Puntos: $sum")
Thanks in advance

Any code that needs data from the database needs to be inside onDataChange or be called from there. Code outside of onDataChange will (most likely) run before the data is loaded.
So:
val userlog = FirebaseAuth.getInstance().currentUser?.displayName
val alertDialogInfo = AlertDialog.Builder(this).create()
myref.orderByChild("userId").equalTo(userlog).addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
var sum = 0
for (data in dataSnapshot.children) {
sum += data.child("puntos").getValue(Int::class.java)!!
}
alertDialogInfo.setTitle(userlog)
alertDialogInfo.setMessage("Puntos: ") // + sum
alertDialogInfo.setButton(
AlertDialog.BUTTON_POSITIVE, "OK"
) { dialog, _ ->; dialog.dismiss() }
alertDialogInfo.show()
val btnPositive = alertDialogInfo.getButton(AlertDialog.BUTTON_POSITIVE)
val layoutParams = btnPositive.layoutParams as LinearLayout.LayoutParams
layoutParams.weight = 100f
btnPositive.layoutParams = layoutParams
}
override fun onCancelled(databaseError: DatabaseError) {
throw databaseError.toException(); // never ignore errors
}
})
For a longer explanation see:
getContactsFromFirebase() method return an empty list
Setting Singleton property value in Firebase Listener

Related

Retrieving data from push() in firebase kotlin that equal to the selected value

Hello can someone that can help me to retrieve the data of selected value from generate key or push in Realtime Database Firebase. Nothings happen when I clicking the button I think my child is not order correctly. This is the collection tree from firebase;
val btnSyncing = findViewById<TextView>(R.id.btnSync)
btnSyncing.setOnClickListener {
val user = FirebaseAuth.getInstance().currentUser
val userid = user!!.uid
database = FirebaseDatabase.getInstance()
reference = database.getReference("Profile")
val timeTextView = findViewById<TextView>(R.id.txtTime)
val dateTextView = findViewById<TextView>(R.id.txtDate)
reference.child(userid)
.child("Delivery Details").orderByChild("Date").equalTo("24/9/22")
.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val time = dataSnapshot.child("Time").value as String?
val date = dataSnapshot.child("Details").value as String?
timeTextView.text = time
dateTextView.text = date
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(baseContext, "Something wrong happened!", Toast.LENGTH_LONG)
.show()
}
})}

Adding document İD to firebase document as a field

I have a model with default values. My app gets the data from user through EditTexts and add them to Firebase Firestore. I hava an addData function (in AddAnalyzeActivity) and savefunction (in AddAnalyzeViewModel) for this operation. I'm getting EditText entries in AddAnalyzeActivity and adding them to my model but on this step ı want to add document id to my model but I can't access the documentIds properly in AddAnalyzeActivity. I can only access them with a forEach method when I try to retrieving the mentioned data with retrieveData function (in PairDetailVM) from Firestore but If I try to add document Ids in retrieveData method it only adds default value of documentId.
What I tried to:
Using #DocumentId annotation in my model.
Setting null default value of documentId in my model.
Getting a list of all documents' ids but can't match them with actual items.
Here is the screenShot for logic:
AnalyzeModel:
data class AnalyzeModel(
var concept: String?="",
var reason: String?="",
var result: String?="",
var rrRatio: Double?=0.0,
var tarih: Timestamp=Timestamp.now(),
var tradingViewUrl: String="",
var id : String="")
addData :
fun addData(view: View) {
val tarih = com.google.firebase.Timestamp.now()
val rr = rrText.text.toString()
var doubleRR = rr.toDoubleOrNull()
if (doubleRR == null) { doubleRR = 0.0 }
val analyzeDTO = AnalyzeModel(
conceptText.text.toString(),
reasonForText.text.toString(),
resultAddingText.text.toString(),
doubleRR,
tarih,
chartImage.text.toString()
)
viewModel.save(analyzeDTO)
val intent = Intent(this, PairDetailActivity::class.java)
startActivity(intent)
finish()
}
save :
fun save(data: AnalyzeModel) {
database.collection(dbCollection!!).document("Specified").collection("Pairs")
.document(chosenPair!!)
.collection("Analysis")
.add(data)
.addOnFailureListener { exception ->
exception.printStackTrace()
Toast.makeText(getApplication(), exception.localizedMessage, Toast.LENGTH_LONG).show()
}
}
retrieveData:
private fun retrieveData() {
val docRef = collectionRef.orderBy("tarih", Query.Direction.DESCENDING)
docRef.addSnapshotListener { value, error ->
try {
if (value != null && !value.isEmpty) {
val allAnalysis= ArrayList<AnalyzeModel>()
val documents = value.documents
documents.forEach {
val analyze = it.toObject(AnalyzeModel::class.java)
if (analyze!=null){
allAnalysis.add(analyze)
}
}
list.value = allAnalysis
} else if (error != null) {
Toast.makeText(Application(), error.localizedMessage, Toast.LENGTH_LONG).show()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
If you want to save the document ID into the document itself, consider separating the creation of the new DocumentReference from writing to it, by using set instead of add.
fun save(data: AnalyzeModel) {
val newRef = database.collection(dbCollection!!).document("Specified").collection("Pairs")
.document(chosenPair!!)
.collection("Analysis")
.document() // 👈 generates a new reference with a unique ID
data.id = newRef.id // 👈 set the ID into your object
newRef.set(data) // 👈 writes the data to the new reference
.addOnFailureListener { exception ->
exception.printStackTrace()
Toast.makeText(getApplication(), exception.localizedMessage, Toast.LENGTH_LONG).show()
}
}
Also see the second snippet in the documentation on adding a document

Paging3 - How to do Reverse Pagination in Chat App

I am recently migrate to Paging3. However, I noticed that most of the tutorials and guides are mainly on normal pagination (from top to bottom).
I need to implement the REVERSE pagination as user scroll to top boundary, will load for page 2,3,4..
Is there any tutorial/guide for this?
PS: Now the initial loading is working fine, but when I scroll to top-most, I have no idea how to load Page 2 data.
My current approach
PagingSource
class ChatPagingSource(
private val apiService: ApiService,
private val roomId: String
): PagingSource<Int, Message>() {
override fun getRefreshKey(state: PagingState<Int, Message>): Int? = null
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Message> {
return try{
val page = params.key?: 1
val pageSize = params.loadSize
val call = apiService.getMessageFeedPaging(
room_id = roomId,
page = page,
max = pageSize,
exclude = EXCLUDE_TYPE
)
val repoItems = call.messages
val prevKey = if(page > 1) page - 1 else null
val nextKey = if(repoItems.isNotEmpty()) page + 1 else null
if(repoItems.isNotEmpty()) {
val messageList = mutableListOf<Message>()
for (i in repoItems) {
val replyData = Converters.convertReplyDataAPItoReplyData(i.reply_data)
val msg = Converters.convertMessageAPItoMessage(replyData, i, hasError = false)
messageList.add(msg)
}
LoadResult.Page(messageList, prevKey, nextKey)
} else {
LoadResult.Page(listOf(), prevKey, nextKey)
}
}catch (e: Exception) {
LoadResult.Error(e)
}
}
}
Repository
fun loadRemoteMessageStream(roomId: String): LiveData<PagingData<Message>> {
return Pager(
config = PagingConfig(20),
pagingSourceFactory = { ChatPagingSource(apiService, roomId) }
).liveData
}
ViewModel
private val _remoteMessage = chatRepository.loadRemoteMessageStream(currentRoomId)
.cachedIn(viewModelScope)
.let { it as MutableLiveData<PagingData<Message>> }
val remoteMessage: LiveData<PagingData<Message>> = _remoteMessage
Fragment
chatViewModel.remoteMessage.observe(viewLifecycleOwner, {
chatAdapter.submitData(viewLifecycleOwner.lifecycle, it)
})
In case this helps anyone, I will post out my own answer.
The key is to reverse prevKey and nextKey and fixed the pageSize that your API required (in my case is 20).
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Message> {
val pageSize = 20
val prevKey = if(repoItems.isNotEmpty()) page + 1 else null
val nextKey = if(page > 1) page - 1 else null
}
Then, in the recyclerview, you should use stackFromEnd = true so that initially the recyclerview will auto-scroll to bottom.

Firebase query equalTo() or startAt().endAt() stop working

I already tried suggestions from other posts but no success.
I just run into an issue,I got the following code which displays data from Firebase database when user clicks on a specific date.If I comment out (startAt(),endAt()), the recycler gets populated with all data, when I add equalTo() or startAt().endAt()) recycler does not get populated.
The code before was working normally, just recently stopped working and I have no clue why.
The code looks like this:
Bellow is the adapter:
fun calendarAdapt(options:FirebaseRecyclerOptions<ModelCalendar>,dataref:
DatabaseReference,context: Context):FirebaseRecyclerAdapter<ModelCalendar,CalendHold>{
return object :FirebaseRecyclerAdapter<ModelCalendar,CalendHold>(options){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CalendHold {
return
CalendHold(LayoutInflater.from(parent.context).inflate(R.layout.list_row_calendar,parent,false))
}
override fun onBindViewHolder(holder: CalendHold, positionc: Int, modelc: ModelCalendar)
{
val postkeyC= getRef(positionc).key
dataref.child(postkeyC!!).addValueEventListener(object:ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
holder.bindlogC(modelc)
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
}
}
This is the Query:
fun query2(cont: LifecycleOwner,selected_date:String):FirebaseRecyclerOptions<ModelCalendar>{
val queryy = FirebaseDatabase.getInstance()
.reference
.child("Ev")
.startAt(selected_date)
.endAt(selected_date)
return FirebaseRecyclerOptions.Builder<ModelCalendar>()
.setQuery(queryy,ModelCalendar::class.java)
.setLifecycleOwner(cont)
.build()
}
Here I apply the adaptor:
mDb = FirebaseDatabase.getInstance().reference.child("Ev")
val c = Calendar.getInstance()
val year2 = c.get(Calendar.YEAR)
val month2 = c.get(Calendar.MONTH)
val day2 = c.get(Calendar.DAY_OF_MONTH)
val dayd= checkDigit(day2)
val monthd= checkDigit(month2+1)
val datetoday = java.lang.StringBuilder()
.append(dayd)
.append("-")
.append(monthd)
.append("-")
.append(year2)
calendinf(datetoday.toString())
calendarid.setOnDateChangeListener { view, year, month, dayOfMonth ->
val month = checkDigit(month+1)
val day = checkDigit(dayOfMonth)
val datesel =StringBuilder()
.append(day)
.append("-")
.append(month)
.append("-")
.append(year)
calendinf(datesel.toString())
}
}
private fun calendinf(selected_date:String){
val options = Queryess().query2(this,selected_date)
val adaptorC = Adaptall().calendarAdapt(options, mDb,this)
calerec!!.adapter=adaptorC!!
}
I fix-it,It was the query, was missing "orderBychild()" field, most probable I mistakenly deleted otherwise cannot explain-it :))

Get random entries in firebase real-time database

This is my code to get 5 items from realtime database:
val database = FirebaseDatabase.getInstance()
val brandReference = database.getReference("brandGame").limitToFirst(5)
brandReference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
dataSnapshot.children.forEach {
...
}
}
}
And this is how my real-time database looks like:
What's the best way to get 5 items randomly? I know there isn't a random function in real time database yet.
If you know the number of elements in the brandGame/-reference, you could pick 5 random numbers between 1 and numberOfElements and retrieve those. This would result in multiple calls to the database.
Alternatively, you could download everything from the brandGame/-reference and just pick 5 random elements using pure Kotlin. But then you must download everything in the reference, which could be a lot.
The best option is to set up a cloud function that does the "pick 5 random options"-logic server side. https://firebase.google.com/docs/functions/ But this requires that you write some js :)
As you say, there is no built-in way to get random elements from a reference.
To get a random brand, please use the following code user side:
val rootRef = FirebaseDatabase.getInstance().reference
val brandGameRef = rootRef.child("brandGame")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val brandCountList = ArrayList<String>()
for (ds in dataSnapshot.children) {
val brand = ds.child("brand").getValue(String::class.java)
brandCountList.add(brand!!)
}
val brandCount = brandCountList.size
val randomNumber = Random().nextInt(brandCount)
val randomBrand = ArrayList<String>()
randomBrand.add(brandCountList.get(randomNumber)) //Add the brand product to list
val arrayAdapter = ArrayAdapter(applicationContext, android.R.layout.simple_list_item_1, randomBrand)
list_view.adapter = arrayAdapter
}
override fun onCancelled(databaseError: DatabaseError) {
//Handle exceptions
}
}
brandGameRef.addListenerForSingleValueEvent(valueEventListener)

Resources