MutableList remaining empty (?) even though i'm populating it - android

I'm trying to populate a mutable list so that I can use it for a recycler view. Unfortunately, although (i think) I'm populating the list, it's still remaining empty and the recycler view is not working (and I imagine it's because of the list issue). Please see below for the code:
private val newList: MutableList<NewListModel> = mutableListOf()
private val oldList = retrieveOldList()
private fun retrieveAndPopulate() {
for (i in 0 until oldList.size){
val oldItem = oldList[i]
val itemOne = oldItem.itemOne
val itemTwo = oldItem.itemTwo
val itemThree = oldItem.itemThree
val itemFour = oldItem.itemFour
val newItemData =
NewListModel(
itemOne, itemTwo, itemThree, itemFour
)
newList.add(newItemData)
Log.d(
"RetrieveData",
"${newItemData.itemOne} has been added to the list."
)
}
}
The class below is for the "NewListModel"
#Keep
#IgnoreExtraProperties
data class NewListModel (
var itemOne: String ?= null,
var itemTwo: String ?= null,
var itemThree: String ?= null,
var itemFour: String ?= null,
)
Below is how i try to populate the "oldList"
fun retrieveData(): MutableList<OldListModel> {
val list: MutableList<OldListModel> = mutableListOf()
val ref = FirebaseDatabase.getInstance().getReference("/storage")
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
ref.get()
.addOnSuccessListener {
for (listItem in snapshot.children) {
val listItem = snapshot.getValue(OldListModel::class.java)
if (listItem != null) {
list.add(listItem)
}
}
}
} else {
Log.d(
"Data",
"Retrieving data was unsuccessful."
)
}
}
override fun onCancelled(error: DatabaseError) {
}
})
return list
}
It's probably worth mentioning that I'm getting the data from one mutable list and adding it to another. Any help is much appreciated
(below is how i try to populate the recycler view)
val newList = retrieveAndPopulate()
val recyclerView = findViewById<View>(R.id.recyclerView) as RecyclerView
val layoutManager = LinearLayoutManager(this)
recyclerView.layoutManager = layoutManager
val adapterAdapter = AdapterAdapter(newList)
recyclerView.adapter = adapterAdapter

Your problem is that you think you're running code sequentially when it's running asynchronously. See the numbered comments from your function to trace the order of execution:
fun retrieveData(): MutableList<OldListModel> {
// 1. Here you create a list
val list: MutableList<OldListModel> = mutableListOf()
val ref = FirebaseDatabase.getInstance().getReference("/storage")
// 2. Here a listener is added that will let you know LATER when the data is ready
ref.addValueEventListener(object : ValueEventListener {
// 4. LATER the data changed will get called
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
ref.get()
.addOnSuccessListener {
// 5. EVEN LATER this listener is called with data
for (listItem in snapshot.children) {
val listItem = snapshot.getValue(OldListModel::class.java)
// 6. FINALLY - you add to a list that has long since stopped being relevant
if (listItem != null) {
list.add(listItem)
}
}
}
} else {
Log.d(
"Data",
"Retrieving data was unsuccessful."
)
}
}
override fun onCancelled(error: DatabaseError) {
}
})
return list // 3. Here you return the EMPTY list that was created
}
A solution - though likely not the best solution is to update your list once the callbacks complete:
private val theList = mutableListOf<YourDataModelType>
fun retrieveData(): { // No longer returning anything
// Remove this, no longer returning anything
// val list: MutableList<OldListModel> = mutableListOf()
val ref = FirebaseDatabase.getInstance().getReference("/storage")
ref.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
ref.get()
.addOnSuccessListener {
theList.clear() // Clear out existing data
for (listItem in snapshot.children) {
val listItem = snapshot.getValue(OldListModel::class.java)
if (listItem != null) {
theList.add(listItem)
}
}
// Since you're using Kotlin, you could use a map,
// but that's unrelated to this issue
// val list = snapshot.children.map { getValue(...) }.filterNotNull()
// Now that we have a full list here, update:
updateAdapterWithNewData(theList)
}
} else {
Log.d(
"Data",
"Retrieving data was unsuccessful."
)
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
Where updateAdapterWithNewData is a function you write to do as it says.
Please read up on asynchronous programming and make sure you understand how the code is flowing when using callbacks / listeners in frameworks like Firebase.

The issue that I had was with regards to not using callbacks while trying to access data in an onSuccessListener. This was causing the list not to be updated at all.
After a day of scrolling on the internet, I finally found these solutions:
Solution: link
Extension to the solution: link
This solved my problem and I hope it solves yours too!

Related

When i get 10 data next 10 data is downloading but the previous 10 data is disappear how can i fix?(I think something wrong with arraylist)

This is my OnCreateActivity(I didn't put any more to avoid confusion)............................................................................................................................
auth= FirebaseAuth.getInstance()
adapters= ImageAdp(ArrayList())
binding.idRLWallpapers.adapter=adapters
getUsers(null)
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
var linearLayoutManager:LinearLayoutManager= recyclerView.layoutManager as LinearLayoutManager
var currentItems=linearLayoutManager.childCount
total_item=linearLayoutManager.itemCount
var lastVisibleitem=linearLayoutManager.findFirstVisibleItemPosition()
if(!isLoadingEDMT&&total_item<=lastVisibleitem+ITEM_COUNT){
// misLoad=true
getUsers(adapters.lastItemId())
isLoadingEDMT=true
binding.bottomPB.visibility=View.GONE
isLoad=false
}
}
})
This is how i am fetching data from firebase database .
private fun getUsers(nodeId:String?) {
if (nodeId==null){
Query = database.orderByKey().limitToFirst(ITEM_COUNT)
}
else{
Query = database.orderByKey().startAfter(nodeId).limitToFirst(ITEM_COUNT)
}
Query.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
var arrayLists=ArrayList<imagemodel>()
if (snapshot.hasChildren()) {
for (data in snapshot.children) {
val image = data.child("url2").value.toString()
val name = data.child("voice1").value.toString()
val childname = data.child("childname").value.toString()
val id = data.child("id").value.toString()
val model = imagemodel(image, name,
arrayLists.add(model)
}
//adapters= ImageAdp(arrayList)
//binding.idRLWallpapers.adapter=adapters
adapters.addAll(arrayLists)
This is my RecyclerView Adapter(I didn't put any more to avoid confusion)
fun addAll(emp:List<imagemodel>){
var initialSize= myarraylist.size
myarraylist.addAll(emp)
notifyDataSetChanged()
}
fun lastItemId(): String? {
return myarraylist.get(myarraylist.size-1).uid
}
Thank you for your time
As i understand, you are initializing your adapter in OnCreate and trying to initialize again for every 10 element in down below, and last one replaces the previous one.
Initialize adapter with empty arraylist in OnCreate:
auth= FirebaseAuth.getInstance()
//binding.idRLWallpapers.adapter= ImageAdp(arrayList)
adapters= ImageAdp(ArrayList()) // like this
binding.idRLWallpapers.adapter=adapters
getUsers(null)
Turn your arrayList to local list to become empty for every 10 item.
Query.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
var arrayList=ArrayList<imagemodel>() // like this
Delete below 2 lines to prevent recreate:
//adapters= ImageAdp(arrayList) // You did this in OnCreate
//binding.idRLWallpapers.adapter=adapters // remove these 2 lines
//inside Query.addValueEventListener
adapters.addAll(arrayList)
These should work, if not i am gonna need more of your code to understand it properly.
Declare your array list here .Then Add it to adapter
Query.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
var arrayList=ArrayList<imagemodel>()
//your codes as it is
if (snapshot.hasChildren()) {
for (data in snapshot.children) {
val image = data.child("url2").value.toString()
val name = data.child("voice1").value.toString()
val childname =
data.child("childname").value.toString()
val id = data.child("id").value.toString()
val model = imagemodel(image, name, childname, id)
arrayList.add(model)
}
}
adapter.addAll(arrayList)
adapter.notyfyDataSetHasChanged()
}

How to read asynchronous data from real-time database using android Kotlin?

Here is my code to read asynchronous data from a real-time database using android Kotlin:
class suDetails : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)
su_image.setOnClickListener {
readData(object : MyCallback {
override fun onCallback(imageUrl: String?) {
if (imageUrl != null) {
val imageViewer = Intent(baseContext, suDetails::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", imageUrl)
startActivity(imageViewer)
}
}
})
}
}
fun readData(myCallback: MyCallback) {
val su_resource =intent
val su_res = su_resource.getStringExtra("su_userid")
val suRef = FirebaseDatabase.getInstance().getReference().child("Users").child(su_res!!)
suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if(dataSnapshot.exists()){
su_layout.visibility = View.VISIBLE
val userData = dataSnapshot.getValue(profile_model::class.java)
val imageUrl = userData!!.getImageUrl()
Picasso.get().load(imageUrl).placeholder(R.drawable.ic_baseline_image_200).into(su_image)
su_name.text = userData.getnameOfsu()
Toast.makeText(baseContext, imageUrl, Toast.LENGTH_LONG).show()
myCallback.onCallback(imageUrl)
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
interface MyCallback {
fun onCallback(value: String?)
}
}
I have referred to other questions to read asynchronous data from a real-time database but when I tried the solution I am not able to show any data in my ImageView and textView. I am getting only the blank screen.
The New code after the answer of Tyler V:
class suDetails : AppCompatActivity() {
private var currentImageUrl: String = ""
private var su_res: String = ""
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)
su_res = intent.getStringExtra("su_userid").toString()
// get views
val su_name = findViewById<TextView>(R.id.su_name)
val su_image = findViewById<ImageView>(R.id.su_image)
// onClick launches another activity - if the image
// hasn't loaded yet nothing happens
su_image.setOnClickListener { viewCurrentImage() }
// start the async loading right away - once it is loaded the
// su_layout view will be visible and the view data
// will be populated. It might be good to show a progress bar
// while it's loading
readData()
}
fun readData() {
println("LOG: called readData")
Toast.makeText(baseContext, su_res, Toast.LENGTH_LONG).show()
println("LOG: getting data for ${su_res}")
val suRef = FirebaseDatabase.getInstance()
.getReference()
.child("Users")
.child(su_res)
suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if (dataSnapshot.exists()) {
println("LOG: data snapshot exists")
su_layout.visibility = View.VISIBLE
val userData = dataSnapshot.getValue(profile_model::class.java)
currentImageUrl = userData?.getImageUrl() ?: ""
su_name.text = userData?.getnameOfsu() ?: ""
println("LOG: Got user data ${currentImageUrl}")
if (currentImageUrl.isNotEmpty()) {
Picasso.get()
.load(currentImageUrl)
.placeholder(R.drawable.ic_baseline_image_200)
.into(su_image)
}
} else {
println("LOG: user not found in database")
}
}
override fun onCancelled(error: DatabaseError) {
println("LOG: cancelled")
}
})
}
private fun viewCurrentImage() {
if (currentImageUrl.isEmpty()) return
Toast.makeText(baseContext, currentImageUrl, Toast.LENGTH_LONG).show()
val imageViewer = Intent(baseContext, ImageViewer::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", currentImageUrl)
startActivity(imageViewer)
}
}
The top answer to this related question shows you how to make callbacks, but that doesn't really answer the question of how to use the async data, and isn't really helpful or relevant to this type of problem.
I don't see anything specifically wrong with your callback - but it silently swallows a number of possible error cases (e.g. if the user doesn't exist). The example below has some print statements that should help determine better what is happening.
A cleaner approach than the extra callback interface is to make a separate method to handle the async result. Here is a cleaned up example of how that might look - with some pseudo-code where parts of your example were missing. To help debug, you should get in the habit of using log or print statements if you don't understand what parts of the code are running, or if something doesn't look the way you expect it to.
private var currentImageUrl: String = ""
private var userId: String = ""
private lateinit var su_name: TextView
private lateinit var su_image : ImageView
private lateinit var su_layout : ConstraintLayout
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_su_details)
// get views
su_name = findViewById<TextView>(R.id.su_name)
su_image = findViewById<ImageView>(R.id.su_image)
su_layout = findViewById<ConstraintLayout>(R.id.su_layout)
su_layout.visibility = View.INVISIBLE
// get user id from intent
userId = intent.getStringExtra("su_userid").orEmpty()
// TODO: Handle what to do if userId is empty here!
if( userId.isEmpty() ) {
finish()
}
// onClick launches another activity - if the image
// hasn't loaded yet nothing happens
su_image.setOnClickListener { viewCurrentImage() }
// start the async loading right away - once it is loaded the
// su_layout view will be visible and the view data
// will be populated. It might be good to show a progress bar
// while it's loading
startLoading()
}
private fun startLoading() {
println("LOG: getting data for ${userId}")
val suRef = FirebaseDatabase.getInstance()
.getReference()
.child("Users")
.child(userId)
suRef.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if(dataSnapshot.exists()) {
println("LOG: data snapshot exists")
val userData = dataSnapshot.getValue(profile_model::class.java)
showData(userData)
}
else {
println("LOG: user not found in database")
}
}
override fun onCancelled(error: DatabaseError) {
println("LOG: cancelled")
}
})
}
private fun showData(userData: profile_model?) {
su_layout.visibility = View.VISIBLE
currentImageUrl = userData?.getImageUrl() ?: ""
su_name.text = userData?.getnameOfsu() ?: "Error"
println("LOG: Got user data ${currentImageUrl}")
if( currentImageUrl.isNotEmpty() ) {
Picasso.get()
.load(currentImageUrl)
.placeholder(R.drawable.ic_baseline_image_200)
.into(su_image)
}
}
private fun viewCurrentImage() {
if( currentImageUrl.isEmpty() ) return
val imageViewer = Intent(this, suDetails::class.java)
imageViewer.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
imageViewer.putExtra("su_image", currentImageUrl)
startActivity(imageViewer)
}

KOTLIN: My data do not gets from firebase realtime database

I am trying to get data from my firebase realtime database.
In my database there is a table whose name is "groups" and it has 3 values "datetime", "title", "username".
I want to get title value and add my list.
Normally in static version, I have a list which includes GroupModelRetrieve types and lists the titles in listview
Now I cant see the titles in listview It is empty
I am new in mobile programming. So, I appreaciate that if you help me.
Here is my code
val list: ArrayList<GroupModelRetrieve> = arrayListOf()
database = FirebaseDatabase.getInstance()
databaseReference = database?.getReference("groups")!!.child("title")
val postListener = object : ValueEventListener{
override fun onCancelled(error: DatabaseError) {
Toast.makeText(requireContext(), "Fail to get data.", Toast.LENGTH_SHORT).show();
}
override fun onDataChange(snapshot: DataSnapshot) {
val data = snapshot.getValue()
val item = GroupModelRetrieve(data.toString())
list.add(item)
}
}
databaseReference!!.addValueEventListener(postListener)
val adapter = GroupsAdapter(requireActivity(), list)
v.findViewById<ListView>(R.id.list_view_groups).adapter = adapter
Also I am adding my model
public class GroupModelRetrieve(Title: String) {
val title:String
init {
title = Title
}
}
If you want, I can add more codes. Thanks for your help.
You need to notify the list with the new changes, as by default Firebase works asynchronously to the main thread, so setting the adapter to the list will get called before any Firebase response.
val adapter = GroupsAdapter(requireActivity(), list)
val listView = v.findViewById<ListView>(R.id.list_view_groups) // << Set listView in a variable
listView.adapter = adapter
val list: ArrayList<GroupModelRetrieve> = arrayListOf()
database = FirebaseDatabase.getInstance()
databaseReference = database?.getReference("groups")!!.child("title")
val postListener = object : ValueEventListener{
override fun onCancelled(error: DatabaseError) {
Toast.makeText(requireContext(), "Fail to get data.", Toast.LENGTH_SHORT).show();
}
override fun onDataChange(snapshot: DataSnapshot) {
val data = snapshot.getValue()
val item = GroupModelRetrieve(data.toString())
list.add(item)
adapter.notifyDataSetChanged() // <<<<<<< Here is the change
}
}
databaseReference!!.addValueEventListener(postListener)
Also I suggest using addListenerForSingleValueEvent() instead of addValueEventListener() in case you need the list only once without the need to real-time updates

Realm query update not reflected in RealmRecyclerViewAdapter

Been using realm and it's awesome.
Came up against something. Wondering if I'm doing something wrong.
I have a RealmRecyclerViewAdapter that I'm using to show the results of a realm query. This works perfectly if I add or update records in the realm. I had to setHasFixedSize(false) on the recycler view to get it to update on the fly. Not sure if this is correct but it worked.
Anyway, that's not my issue.
I'm experimenting with filtering my data. I have the following query:
realm.where(Person::class.java).contains("name", nameFilter, Case.INSENSITIVE).findAllSorted("name")
I'm passing this RealmResults to my recycler view and it works great on add/update.
However, when I attempt a filter, it doesn't update automatically.
Am I right in saying that simply changing my filter (specified by nameFilter) isn't enough for the query to be re-run? This would be fair enough I suppose. Since I guess there's no trigger for realm to know I've changed the value of the string.
However, even if I recalculate my query, it doesn't seem to update in the Recycler View unless I explicitly call updateData on my adapter. I'm not sure if this is the best or most efficient way to do this. Is there a better way?
Complete Code:
Main Activity
class MainActivity : AppCompatActivity(), View.OnClickListener {
private val TAG: String = this::class.java.simpleName
private val realm: Realm = Realm.getInstance(RealmConfiguration.Builder().deleteRealmIfMigrationNeeded().build())
private var nameFilter = ""
private var allPersons: RealmResults<Person> = realm.where(Person::class.java).contains("name", nameFilter, Case.INSENSITIVE).findAllSorted("name")
private val adapter: PersonRecyclerViewAdapter = PersonRecyclerViewAdapter(allPersons)
private lateinit var disposable: Disposable
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
realm.executeTransaction({
// realm.deleteAll()
})
Log.i(TAG, "Deleted all objects from Realm")
buttonAddOrUpdatePerson.setOnClickListener(this)
setUpRecyclerView()
disposable = RxTextView.textChangeEvents(editTextNameFilter)
// .debounce(400, TimeUnit.MILLISECONDS) // default Scheduler is Computation
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith<DisposableObserver<TextViewTextChangeEvent>>(getSearchObserver())
}
private fun getSearchObserver(): DisposableObserver<TextViewTextChangeEvent> {
return object : DisposableObserver<TextViewTextChangeEvent>() {
override fun onComplete() {
Log.i(TAG,"--------- onComplete")
}
override fun onError(e: Throwable) {
Log.i(TAG, "--------- Woops on error!")
}
override fun onNext(onTextChangeEvent: TextViewTextChangeEvent) {
nameFilter = editTextNameFilter.text.toString()
allPersons = realm.where(Person::class.java).contains("name", nameFilter, Case.INSENSITIVE).findAllSorted("name")
// this is necessary or the recycler view doesn't update
adapter.updateData(allPersons)
Log.d(TAG, "Filter: $nameFilter")
}
}
}
override fun onDestroy() {
super.onDestroy()
realm.close()
}
override fun onClick(view: View?) {
if(view == null) return
when(view) {
buttonAddOrUpdatePerson -> handleAddOrUpdatePerson()
}
}
private fun handleAddOrUpdatePerson() {
val personToAdd = Person()
personToAdd.name = editTextName.text.toString()
personToAdd.email = editTextEmail.text.toString()
realm.executeTransactionAsync({
bgRealm -> bgRealm.copyToRealmOrUpdate(personToAdd)
})
}
private fun setUpRecyclerView() {
recyclerViewPersons.layoutManager = LinearLayoutManager(this)
recyclerViewPersons.adapter = adapter
recyclerViewPersons.setHasFixedSize(false)
recyclerViewPersons.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL))
}
}
PersonRecyclerViewAdapter
internal class PersonRecyclerViewAdapter(data: OrderedRealmCollection<Person>?, autoUpdate: Boolean = true) : RealmRecyclerViewAdapter<Person, PersonRecyclerViewAdapter.PersonViewHolder>(data, autoUpdate) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PersonViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.person_row, parent, false)
return PersonViewHolder(itemView)
}
override fun onBindViewHolder(holder: PersonViewHolder?, position: Int) {
if(holder == null || data == null) return
val personList = data ?: return
val person = personList[position]
holder.bind(person)
}
internal class PersonViewHolder(view: View) : RecyclerView.ViewHolder(view) {
var textViewName: TextView = view.findViewById(R.id.textViewNameDisplay)
var textViewEmail: TextView = view.findViewById(R.id.textViewEmailDisplay)
internal fun bind(person: Person) {
textViewEmail.text = person.email
textViewName.text = person.name
}
}
}
Yeah, updateData() is the way to do it. Since you updated the query, the Results you want to show becomes a different object. updateData() has to be called to notify the adapter that the data source is changed.
However, you may lose the nice animation for the RecyclerView in this way since the whole view will be refreshed because of the data source is changed. There are some ways to work around this.
eg.: You can add one field isSelected to Person. Query the results by isSelected field and pass it to the adaptor:
allPersons = realm.where(Person::class.java).equalTo("isSelected", true).findAllSorted("name")
adapter = PersonRecyclerViewAdapter(allPersons)
When changing the query:
realm.executeTransactionAsync({
var allPersons = realm.where(Person::class.java).equalTo("isSelected", true).findAllSorted("name")
for (person in allPersons) person.isSelected = false; // Clear the list first
allPersons = realm.where(Person::class.java).contains("name", nameFilter, Case.INSENSITIVE).findAllSorted("name") // new query
for (person in allPersons) person.isSelected = true;
})
It depends on your use case, if the list to show is long, this approach might be slow, you could try to add all the filtered person to a RealmList and set the RealmList as the data source of the adapter. RealmList.clear() is a fast opration than iterating the whole results set to set the isSelected field.
If the filter will mostly cause the whole view gets refreshed, updateData() is simply good enough, just use it then.

Firebase database query design

I have basically a list of entries that many users can read (but can't write). These items show up sorted in the app based on a unique integer that each entry has. I'd like to add a way to allow each individual user to favorite some x number of these items, making those x items appear first in the list. Is there a way I can achieve this using firebase's querying without having to duplicate the list for each user?
Rather than using FirebaseRecyclerAdapter and just passing in the query (which is what I was previously doing), I had to manually manage the results and use a RecyclerView.Adapter instead. This is the code that I ended up with. I'll clean it up a bit so it actually replaces items if they're already there and update and whatnot, but this is a quick summary of the necessary approach:
val data = ArrayList<SomeData>
val favorites = ArrayList<Favorite>
val dataWithFavorites = ArrayList<SomeData>
private val dbRef : DatabaseReference by lazy { FirebaseDatabase.getInstance().reference }
private val dataQuery : Query by lazy { dbRef.child("data").orderByChild("rank") }
private val favoritesQuery : Query by lazy { dbRef.child("users").child("$someUserId").child("favorites").orderByChild("name") }
init {
dataQuery.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError?) { }
override fun onDataChange(p0: DataSnapshot?) {
data.clear()
p0!!.children.mapTo(dataList) { it.getValue(SomeData::class.java)!! }
updateEntries()
}
})
favoritesQuery.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError?) { }
override fun onDataChange(p0: DataSnapshot?) {
favorites.clear()
p0!!.children.mapTo(dataList) { it.getValue(Favorite::class.java)!! }
updateEntries()
}
})
}
private fun updateEntries() {
if (data.isEmpty()) {
return
}
val favoritesStrings = favorites.map { (id) -> id }
val favoriteData = data
.filter { favoritesStrings.contains(it.id) }
.sortedBy { it.name }
.onEach { it.isFavorite = true }
dataWithFavorites.clear()
dataWithFavorites.addAll(data)
dataWithFavorites.removeAll(favoriteData)
dataWithFavorites.forEach { it.isFavorite = false }
dataWithFavorites.addAll(0, favoriteData)
recyclerView.adapter?.notifyDataSetChanged()
}

Categories

Resources