Kotlin - How to save data from firebase to livedata with data class? - android

I trying run this code, but I get error: Can't convert object of type java.lang.String
i need to fetch data from realtime database and save it to livedata.
fun loadRecentlyAddedVoices(){
REF_DATABASE_ROOT.child(NODE_STICKERS).addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (ds in snapshot.children) {
recentlyAddedVoices.value = ds.getValue(VoicesModel::class.java).toString()
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
Voices Model:
data class VoicesModel(
val title:String,
val description: String,
val count: Int,
val likes: Int,
val downloads: Int,
val data: VoiceDataModel
)
VoiceDataModel:
data class VoiceDataModel(
val voice_title: String,
val source: String
)

Can't convert object of type java.lang.String
According to your shared code, this exception is most probably given from the below:
recentlyAddedVoices.value = ds.getValue(VoicesModel::class.java).toString()
recentlyAddedVoices looks like the MutableLiveData object of a list of VoiceDataModel.
Using toString() here is the reason of the raised exception. And there is no reason to convert this value to a String.
Instead you need to create a normal list of VoicesModel first by iterating over the children of the datasnapshot, and then set the LiveData upon the completion of the list:
fun loadRecentlyAddedVoices(){
REF_DATABASE_ROOT.child(NODE_STICKERS).addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val temp: MutableList<VoicesModel> = mutableListOf()
for (ds in snapshot.children) {
val value: voiceModel = ds.getValue<VoicesModel>(VoicesModel::class.java)
// Aggregating to a non-live data list
temp.add(value)
}
// Setting the Live data value:
recentlyAddedVoices.value = temp
}
override fun onCancelled(error: DatabaseError) {
}
})
}

Related

Getting data from firebase in androd studio

How to get data from firebase?
I can implement some data on it, but I do now know why I cannot get and put it in my activity, application.
This is my ValuEventListener, should I use here Livedata?
class FirebaseDB : LiveData<List<Shopping>>() {
var fbItemCount:Long = 0
private val firebaseDB2: FirebaseDatabase = FirebaseDatabase.getInstance()
private val userID = FirebaseAuth.getInstance().uid
public val ref = firebaseDB2.getReference("user/"+userID.toString())
fun removeAll() {
}
fun delete(shopping: Shopping){
}
fun modify(shopping: Shopping) {
}
fun add(shopping: Shopping) {
ref.child(shopping.id.toString()).setValue(shopping)
}
fun getShopping(): List<Shopping> {
val lista: ArrayList<Shopping> = ArrayList()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})
fbItemCount = lista.size.toLong()
return lista
}
}
How can i check if it is downloaded somewhere on my app or I just cannot see it or implement well.
EDIT:
I just added log.v... and it starts working...
However after adding a new element in the list, new element is added with double information from firebase for example:
In database I have:
Orange
Apples
I add:
Bananas
Now in application it is like:
Orange
Apples
Orange
Apples
Bananas
onDataChange is an async callback and does not result in the same thread, so return one liveData and observe it, then in the on data change process the list and post it on the liveData.
fun getShopping(): LiveData<List<Shopping>> {
val lista: ArrayList<Shopping> = ArrayList()
val data=MutableLiveData<List<Shopping>>()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
fbItemCount = lista.size.toLong()
data.postValue(lista)
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})
return data
}
About your double items problem this code making it because before making ValueEventListener you created an empty list then after get data from firebase in onDataChange method you add all items that firebase returns then suppose you add a new item by calling 'add' method them onDataChange will be triggered again and giving you again all items with a new one added, but you never remove the old item and add these new items again to this list. That's why it's happening
val lista: ArrayList<Shopping> = ArrayList()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})
Correct code would be like this:
var lista = emptyList<Shopping>()
ref.addValueEventListener(object : ValueEventListener{
override fun onDataChange(dataSnapshot: DataSnapshot) {
lista = emptyList()
for (messageSnapshot in dataSnapshot.children) {
val shopping: Shopping = Shopping(id = messageSnapshot.child("id").value as Long,
product = messageSnapshot.child("product").value as String,
quantity = messageSnapshot.child("quantity").value as String,
price = messageSnapshot.child("price").value as String,
bought = messageSnapshot.child("bought").value as Boolean)
//Log.i("readDB", "$product $quantity $price $isbought")
lista.add(shopping)
}
}
override fun onCancelled(error: DatabaseError) {
Log.e("MyAdapter", "Failed to delete value.",error.toException())
}
})

Can't convert object of type java.lang.Long to type(Model class); Fetching Data from firebaseDatabase android kotlin

I am using Firebase Database to populate a recyclerview. and i'm facing problem in fetching data.
the error
com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.Long to type com.massino.pfeadelramzi.models.Meuble
my code:
var mdatabase : DatabaseReference?=null
var listMeubles = mutableListOf<Meuble>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_liste_meuble3_d)
mdatabase = FirebaseDatabase.getInstance().reference.child("Bureau")
mdatabase!!.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (sna: DataSnapshot in snapshot.children){
val lis: Meuble? = sna.getValue(Meuble::class.java) //THE PROBLEME IS HERE
listMeubles.add(lis!!)
}
mon_recycler.setHasFixedSize(true)
mon_recycler.layoutManager = LinearLayoutManager(this#ListeMeuble3DActivity)
mon_recycler.adapter = MeubleAdapter(listMeubles.toTypedArray()){}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
}
My data Class Meuble:
data class Meuble(val imageResource: Int, val nom: String, val prix: Int,val stock:Int)
my Firebase data (now i'm just trying to find a solution so i created just one child
Code to add data to Firebase
button4.setOnClickListener{
var nomUI = spinner.selectedItem.toString()
var prixUI = textView2.text.toString().toInt()
var stockUI= textView3.text.toString().toInt()
var databaseref = firebaseDatabase.getReference(nomUI)
if ( nomUI != null || !TextUtils.isEmpty(prixUI.toString()) || !TextUtils.isEmpty(stockUI.toString())){
var meuble = Meuble(R.drawable.fauteuille2,nomUI,prixUI,stockUI)
databaseref.setValue(meuble)
}else {
Toast.makeText(this,"Remplissez la case manquante",Toast.LENGTH_LONG).show()
}
}
Please help to solve this exception.
As Ticherhaz commented: since you only have the properties for one child nodes under /Bureau, you should use loop over the child nodes in onDataChange.
Right now your loop means that sna points to each individual property, and that's why it fails. There is (for example) no way to parse the value of prix into a `` object.
The code for when you have a single child is:
mdatabase = FirebaseDatabase.getInstance().reference.child("Bureau")
mdatabase!!.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val lis: Meuble? = snapshot.getValue(Meuble::class.java)
listMeubles.add(lis!!)
mon_recycler.setHasFixedSize(true)
mon_recycler.layoutManager = LinearLayoutManager(this#ListeMeuble3DActivity)
mon_recycler.adapter = MeubleAdapter(listMeubles.toTypedArray()){}
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
Error Handling :
So to handle my error:
i had to add a Default Value to each parametre in the constructor of my Data class :
data class Meuble(val imageResource: Int = -1, val nom: String="", val prix: Int=-1,val stock:Int=-1)

How to retrieve nested data structure from Firebase Realtime Database on Kotlin

I'm trying to retrieve submittedRequests from database .
userRef.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot!!.exists()){
val children=snapshot!!.children
for(item in children) {
val retrieveUser= item.getValue(User::class.java) //it crashes here
if (retrieveUser != null) {
userData.add(retrieveUser)
}
}
}
}
})
User Class
class User(val userId:String="", val name:String="", val surname:String="", val profileImageUrl: String="",val submittedRequests:String="", val pickedUpRequests:String="")
Error Message is :
com.google.firebase.database.DatabaseException: Failed to convert value of type java.util.HashMap to String
How can I retrieve submittedRequests from database properly?
You're declared submittedRequests:String="" in your User class. Looking at your JSON the submittedRequests is not a simple string, but rather a nested object, or a map.
This should work better
submittedRequests:Map<String, Any>=hashMapOf<String, Any>()

How to correctly add adapter from two references in Kotlin Firebase?

I've been trying to make something like a quiz. After completing a test, results are being saved in this tree by current users' UID. Then a user can open the educational course progress and watch a score number on every completed part of this course(plus they can click on the part and go right to it). I tryed to make it this way
private fun listenForProgress() {
val ref = FirebaseDatabase.getInstance().getReference("/course/$courseUid/parts/")
ref.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
val coursePart = it.getValue(PartOfCourse::class.java)!!
coursePartUid = coursePart.uid
val saveScoreRef = instance
.getReference("/course/$courseUid/parts/$coursePartUid/score/$currentUserUid")
saveScoreRef.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
val courseProgress = it.getValue(ScoreItem::class.java)
adapter.add(ProgressLeft(courseProgress!!))
}
}
override fun onCancelled(p0: DatabaseError) { }
})
}
adapter.setOnItemClickListener { item, view ->
val partOfCourseItem = item as PartOfCourseItem
val intent = Intent(view.context, InfoActivity::class.java)
coursePartUid = partOfCourseItem.partOfCourse.uid
coursePartTitle = partOfCourseItem.partOfCourse.title
intent.putExtra("courseUid", courseUid)
intent.putExtra("coursePartUid", coursePartUid)
intent.putExtra("coursePartTitle", coursePartTitle)
startActivity(intent)
finish()
}
progressRecyclerView.adapter = adapter
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
The first reference works just great, but I have a problem with the second one by getting this kind of exception
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.awesomeproject, PID: 6085
com.google.firebase.database.DatabaseException: Can't convert object of type java.lang.String to type com.example.awesomeproject.models.ScoreItem
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertBean(com.google.firebase:firebase-database##19.3.0:435)
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.deserializeToClass(com.google.firebase:firebase-database##19.3.0:231)
at com.google.firebase.database.core.utilities.encoding.CustomClassMapper.convertToCustomClass(com.google.firebase:firebase-database##19.3.0:79)
at com.google.firebase.database.DataSnapshot.getValue(com.google.firebase:firebase-database##19.3.0:203)
at com.example.awesomeproject.courses.ProgressActivity$listenForProgress$1$onDataChange$$inlined$forEach$lambda$1.onDataChange(ProgressActivity.kt:67)
at com.google.firebase.database.Query$1.onDataChange(com.google.firebase:firebase-database##19.3.0:179)
at com.google.firebase.database.core.ValueEventRegistration.fireEvent(com.google.firebase:firebase-database##19.3.0:75)
at com.google.firebase.database.core.view.DataEvent.fire(com.google.firebase:firebase-database##19.3.0:63)
at com.google.firebase.database.core.view.EventRaiser$1.run(com.google.firebase:firebase-database##19.3.0:55)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
I'm saving data with
#Parcelize
class ScoreItem(
val userUid: String,
val coursePartUid: String,
val score: Int,
val partTitle: String
): Parcelable {
constructor(): this("", "", 0, "")
}
and trying to get it with
class ProgressLeft(
val progress: ScoreItem
): Item<GroupieViewHolder>() {
override fun getLayout(): Int {
return R.layout.progress_left_row_layout
}
override fun bind(viewHolder: GroupieViewHolder, position: Int) {
viewHolder.itemView.scoreTextView.text = progress.score.toString()
viewHolder.itemView.partTitleTextView.text = progress.partTitle
}
}
it is a part of JSON file, where I'm working now
"course" : {
"b2611197-1b36-4908-89c2-9dd067fa66e3" : {
"description" : "Dogs are cute",
"imageCourseUrl" : "https://sun9-66.userapi.com/c851028/v851028425/a6e1a/gadTK-MxieA.jpg",
"parts" : {
"95706cfa-2c4c-4e39-b433-8e13c7212a2d" : {
"imageUrl" : "https://sun9-45.userapi.com/c635100/v635100806/62080/mrLF0IyBzNA.jpg",
"info" : "James Franko has four puppies",
"score" : {
"Vk6IOCAaFMdwdruZrrb9DhmDdDh2" : {
"coursePartUid" : "95706cfa-2c4c-4e39-b433-8e13c7212a2d",
"partTitle" : "First part",
"score" : 2,
"userUid" : "Vk6IOCAaFMdwdruZrrb9DhmDdDh2"
}
As far as I can see your nested listener only loads the score for a single player, so it shouldn't be looping over the child nodes in onDataChange. By doing that, your it becomes an individual property, and trying to read a from a single property value is leading to the message you get.
It more likely should look something like this:
val saveScoreRef = instance.getReference("/course/$courseUid/parts/$coursePartUid/score/$currentUserUid")
saveScoreRef.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onDataChange(scoreSnapshot: DataSnapshot) {
val courseProgress = scoreSnapshot.getValue(ScoreItem::class.java)
adapter.add(ProgressLeft(courseProgress!!))
}
override fun onCancelled(error: DatabaseError) {
throw error.toException();
}
A few more notes on the above:
Please never leave onCancelled empty, as you may be hiding important problems that way.
When you attach the outer listener to /course/$courseUid/parts/, its onDataChange gets called with all data under that location. So you shouldn't need a nested listener, but can instead get all data from the original p0 (I highly recommend giving parameters more meaningful names than these defaults, as I've done above with `scoreSnapshot).

Populating Data Class via Simple Firebase Calls

Running into an error I have been researching and attempting to fix for the past couple weeks. There are tons of suggestions out there and I've tried at least half a dozen with the same result each time.
How does Kotlin access Firebase data and populate a very simple data class?
Error: com.google.firebase.database.DatabaseException:
Can't convert object of type java.lang.String to type com.touchtapapp.handsofhope.LandingTextTitles
Read about suggestions to first convert to a Map and then to my custom data class... attempted this, successfully created the Mapped values w/ correct data... but ran into the exact same error when sending the Mapped values to the customs data class (LandingTextTitles).
Current code:
Data Model Class
data class LandingTextTitles(
val subTitle: String,
val title: String
)
Method to retrieve data from firebase
private fun initTitles() {
val ref = FirebaseDatabase.getInstance().getReference("/landing")
ref.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
val titles = it.getValue(LandingTextTitles::class.java)
}
}
override fun onCancelled(p0: DatabaseError) {
// Handle Cancelled Data
}
})
// Log the titles value to see if data passed correctly
Log.d("Titles", titles.toString())
}
When I log out something like Log.d(it.toString()), I see the keys and values just fine. What am I doing wrong here?
EDIT:
Firebase data snapshot
EDIT 2:
If we use Log.d("Titles", it.toString()), we get the following:
D/Titles: DataSnapshot { key = subTitle, value = Start Here. }
D/Titles: DataSnapshot { key = title, value = Facing unexpected problems? }
If you have the following database:
landing
randomId
subTitle : "Awesome"
title : "Developer Team"
Then you can retrieve title and subTitle by doing the following:
private fun initTitles() {
val ref = FirebaseDatabase.getInstance().getReference("/landing")
ref.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
val title = it.child("title").getValue(String::class.java)
val subTitle = it.child("subTitle").getValue(String::class.java)
}
}
override fun onCancelled(p0: DatabaseError) {
// Handle Cancelled Data
}
})
// Log the titles value to see if data passed correctly
Log.d("Titles", titles.toString())
}
If you want to use the data class, then change this:
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
val titles = it.getValue(LandingTextTitles::class.java)
}
into this:
override fun onDataChange(p0: DataSnapshot) {
val titles = p0.getValue(LandingTextTitles::class.java)
}

Categories

Resources