how to get all child with specific value from Firebase android kotlin? - android

I want to get just color item in the image from all user in my database. I try this
private fun setListPersonOfferService(){
val database = FirebaseDatabase.getInstance()
val userIdReference = database.getReference("user personal data").child("Country")
val userFilterListener = object : ValueEventListener {
override fun onCancelled(databaseError: DatabaseError) {
// handle error
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
val listTest = dataSnapshot.children.map { it.getValue(HomePageModel::class.java)!! }
itemAdapter = ItemAdapterHomePage(context ?: return,
listTest as ArrayList<HomePageModel>
)
itemAdapter?.onItemClick = {
Supplier.serviceList[it].state = !Supplier.serviceList[it].state
itemAdapter?.notifyDataSetChanged()
Toast.makeText(context, "go to speck", Toast.LENGTH_LONG).show()
}
recyclerView_homePage.apply {
layoutManager = LinearLayoutManager(context)
adapter = itemAdapter
}
}
}
userIdReference.addListenerForSingleValueEvent(userFilterListener)
}
class HomePageModel(var lastName: String?=null,
var town: String?=null,
var faculty: String?=null,
var languageOfStudy: String?=null )
if i change Reference to the specific directory .child("Turcia").child("Adana").child("Çukurova University").child("Faculty of Business") it works, but i want to get from all user.

You'll need to navigate over the dataSnapshot in your onDataChange to get to the correct child nodes.
To navigate a DataSnapshot object you have two main approaches:
You already know the key of the child you want to access, in which case you use the child() method to access that specific child node. For example, you could read the marked child in the JSON with dataSnapshot.child("Turcia/Adana/Çukurova University/Faculty of Business").children.map, similarly to what you already tried on the reference.
You don't know the key of the child nodes, in which case you loop over the getChildren() property of the snapshot. For an example of the latter, see the Firebase documentation on listening for value events on a list of children.
I think you're looking for the second approach here, but in any case you're dealing with DataSnapshots, there are the ways to get at the data in there.

Related

Where do I put a addValueEventListener that only gets triggered when there's a change in my Firebase Realtime Database for my fragment?

In my Fragment for my Android app, I'm using Firebase Realtime Database and Moshi to save and load the data I get from my RecyclerView.
These are the functions I use for this task:
private fun saveData() {
val moshi = Moshi.Builder().add(BigDecimalAdapter).add(KotlinJsonAdapterFactory()).build()
val listMyData = Types.newParameterizedType(List::class.java, ItemCard::class.java)
val jsonAdapter: JsonAdapter<ArrayList<ItemCard>> = moshi.adapter(listMyData)
val json = jsonAdapter.toJson(dataList)
userInfo.child("jsonData").setValue(json)
}
private fun loadData(json: String) = lifecycleScope.launch(Dispatchers.IO) {
if (json != "") {
val type: Type = object : TypeToken<List<ItemCard>>() {}.type
val moshi = Moshi.Builder().add(BigDecimalAdapter).add(KotlinJsonAdapterFactory()).build()
val jsonAdapter: JsonAdapter<ArrayList<ItemCard>> = moshi.adapter(type)
dataList = jsonAdapter.fromJson(json)!!
if (dataList == null) {
dataList = arrayListOf<ItemCard>()
}
}
}
private fun buildRecyclerView() {
recyclerView = rootView.findViewById(R.id.main_recycler_view)
recyclerView.setHasFixedSize(true)
recyclerViewLayoutManager = LinearLayoutManager(this#Main.requireContext())
adapter = MainAdapter(dataList, this)
recyclerView.layoutManager = recyclerViewLayoutManager
recyclerView.adapter = adapter
}
In my onViewCreated, I having this for loading the data and building the RecyclerView:
userInfo.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (postSnapshot in dataSnapshot.children) {
when (postSnapshot.key) {
"jsonData" -> {
loadData(postSnapshot.value.toString())
buildRecyclerView()
}
}
}
}
override fun onCancelled(error: DatabaseError) {}
})
Everything works as I want, however, there's a delay/lag when I go to this specific fragment. There are a total of three fragments in my app. The other two work smoothly with no delay/lag, but when I click on the button or slide the screen to go to this fragment, there's a delay in the change of the UI.
What can I do to make the performance better? Where should I put my addValueEventListener? I only want it to get triggered when the fragment is first created and when the jsonData child gets changed. I believe in my onViewCreated the listener is being triggered multiple times. Is there anything else I can add to my code or modify to make the performance better when saving and loading the RecyclerView data?
when you create a listerner in onViewCreated run then make sure that you remove listener when fragment is not attach.Realtime event listerner return a string that you can easily = to your pojo class like:
ItemCard message = messageSnapshot.getValue(ItemCard.class);
this way is to saving manual converting the list effort.Last important thing that if recyclerview is initialize then don't initialize when data change only notifyDataSetChange.when you adding data in the list then make sure that the
list.clear();
otherwise you data is duplicate because on addValueEventListener return the whole data.

Kotlin: How to use RecycleView + ViewAdapter so that different users get different ViewHolders?

I am working on an android app and using Kotlin for the first time. The program does the following:
1) Login
2) Write Data to Database (Firebase Realtime Database)
3) Read Data from Database (RecycleView + Adapter with ViewHolder)
4) Users: Normal, Admin, Special
"Normal" users can input data and look at database objects using RecycleView + ViewHolder; "Admin" can overwrite a specific identifier in objects in the database by clicking a button (which is invisible for other users); "Special" users can only see those objects with that specific identifier in them using RecycleView + ViewHolder.
The problem is: specific RecycleView for "Special" users doesn't work. The Activity just crashes. The Adapter for RecycleView works: data is submitted to the database by "normal" users and data is displayed correctly in TextView + ViewHolder, even after updating through Admin user.
Here is my code (### represent hashes that are hardcoded for testing purposes):
class SchadenAdapter : RecyclerView.Adapter<SchadenViewHolder>() {
[...]
class SchadenViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
fun bind(schaden: Schaden) {
val userId: String = FirebaseAuth.getInstance().currentUser!!.uid
itemView.textViewSchadenort.text = schaden.schadenort
itemView.textViewSchadenart.text = schaden.schadenart
itemView.textViewSchadendatum.text = schaden.datum
itemView.textViewSchadenstatus.text = schaden.status
if (userId == "###") {
itemView.buttonTextViewAllocate.setOnClickListener() {
(R.layout.item_schaden)
// show message box after button click
AlertDialog.Builder(itemView.context)
.setMessage("Schaden zugeteilt.")
.create()
.show()
// get uuid: update "allocate" + "status"
val rootRef = FirebaseDatabase.getInstance().reference
val uuidRef = schaden.uuid
rootRef.child("schäden")
.child(uuidRef)
.child("allocated")
.setValue("###")
rootRef.child("schäden")
.child(uuidRef)
.child("status")
.setValue("###")
}
} else {
itemView.buttonTextViewAllocate.setVisibility(View.GONE)
}
}
}
At first I tried to do the same for "Special" users: I created a button in the layout.xml and just used the same structure in the Adapter - the only difference is that they can only change "status" by clicking a button. But everytime I tried to do so, the app crashed.
For the sake of completeness, here is the code for the Activity that uses the Adapter:
class SchadenListeActivity : AppCompatActivity() {
private lateinit var databaseReference: DatabaseReference
private lateinit var schadenAdapter: SchadenAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_schaden_liste)
databaseReference = FirebaseDatabase.getInstance().reference
schadenAdapter = SchadenAdapter()
recyclerViewSchadenliste.layoutManager = LinearLayoutManager(this)
recyclerViewSchadenliste.adapter = schadenAdapter
val itemDecor = DividerItemDecoration(this, VERTICAL)
recyclerViewSchadenliste.addItemDecoration(itemDecor)
databaseReference.child("schäden")
.addValueEventListener(object : ValueEventListener {
override fun onCancelled(databaseError: DatabaseError) {
Toast.makeText(this#SchadenListeActivity,
"Database error: $databaseError.message", Toast.LENGTH_SHORT).show()
}
override fun onDataChange(dataSnapshot: DataSnapshot) {
val schadenListe = dataSnapshot.children.mapNotNull { it.getValue<Schaden>(Schaden::class.java) }
schadenAdapter.setSchaden(schadenListe)
}
})
}
}
Help is greatly appreciated.
I am not exactly sure of the app crash, what I can observe seeing your code is your are trying to show alert dialog using itemView.context which is not possible. You can show a dialog using activity context only. Try to show the dialog using activity context and check if its still crashing.
Please provide the stacktrace of your current app crash.

how to check if firebase have value1 and value2 exist

I'm trying to do the following in my Android application using Kotlin :
I have the Firebase data structure as shown in this image:
The user is able to choose 2 stations from the stations included in the data, either as "start_station", "end_station" or "station_number".. in the next function am trying to take the user's selections and check if both are encluded in the same line.
private fun fetchingLinesData(
theEndStation: String,
theStartStation: String,
) {
val database = FirebaseDatabase.getInstance()
val myRef = database.getReference("line")
myRef.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
toast(getString(R.string.could_not_find_a_way))
}
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach{
//Here should be the line that checks if both data exist
}
}
})
}
Tried using This hasChild Method but it wasn't right.
Try to convert your response to HashMap and search using containsValue
override fun onDataChange(p0: DataSnapshot) {
p0.children.forEach {
//Here should be the line that checks if both data exist
if(it.hasChildren()) {
val stationLines = it.value as HashMap<String, Any>
val validStart = stationLines.containsValue(theStartStation)
val validEnd = stationLines.containsValue(theEndStation)
...
}
}
}
You can do a query:
myRef.orderByChild("end_station").equalTo(theEndStation).addValueEventListener
This will retrieve the data according to the query, then inside onDataChange() after the forEach you can do the following:
if(p0.exists()){
// retrieve data here
}
Using exists() you can check if the datasnapshot exists.
This query will only check for end_station, if you want to check both, then after the query inside the if statement you can retrieve the first_station:
if(p0.exists()){
val firstStation = it.child("start_station").value
And check if firstStation is equal to theStartStation

Firebase getValue returns as a HasMap with the key regardless of how I ask for it

Some of my data is stored in Firebase as like in the screen shot bellow, under a parent called "votes":
I am trying to pull the value only (-1) but keep getting the whole HashMap.
The key in this case is represented in my code as a variable called inititorId and postVotesSnapshot represents the parent snapshot the holds many children as in the screen shot I've attached.
I've tried:
postVotesSnapshot.child(initiatorId).value
or
postVotesSnapshot.child(initiatorId).getValue(Integer::class.java)
And both got me the whole HashMap with the key causing a crash because I need the value to be an Int.
I've tried:
val valueHash = postVotesSnapshot.child(initiatorId).getValue(HashMap::class.java)
val myValue = valueHash[initiatorId]
But that doesn't work wither.
I'm not sure what has gone wrong as the code worked perfectly before with the first option I've mentioned and today it suddenly throws an error at me.
Here's the complete Listener:
val refVotes = if (postType == 0) {
FirebaseDatabase.getInstance().getReference("/questions/$mainPostId/main/votes")
} else {
FirebaseDatabase.getInstance().getReference("/questions/$mainPostId/answers/$specificPostId/votes")
}
refVotes.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {
}
override fun onDataChange(postVotesSnapshot: DataSnapshot) {
setVotesCount(specificPostId, mainPostId, votesView, postType)
if (postVotesSnapshot.hasChild(initiatorId)) {
val voteValue = postVotesSnapshot.child(initiatorId).getValue(Integer::class.java) //this line is the problematic one
//I do stuff
}
}
})
}
Try the following:
val ref = firebase.child("posts")
ref.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot?) {
val id = dataSnapshot.child(initiatorId).getValue(Integer::class.java)
}
override fun onCancelled(error: FirebaseError?) {
println(error!!.message)
}
})
Assuming, you have the following database:
posts
VMQPBq6YK3bJ12xIjGeTHsqaJC2 : -1
Here the dataSnapshot will be at child posts, then you need to attach the addListenerForSingleValueEvent and access the child initiatorId. Also assuming that initiatorId is equal to VMQPBq6YK3bJ12xIjGeTHsqaJC2

why firebase display data randomly and unordered?

i don't know why when i get my data from the firebase will Display on the listview random and unordered i tryed many time with out any result
there is no problem with the data on fire base but on the phone display.
fun AddToList(){
mRef.child("MyPosts").child("TEST").addValueEventListener(object
:ValueEventListener{
override fun onCancelled(p0: DatabaseError?) {
Toast.makeText(this#NewsList,"Error",Toast.LENGTH_SHORT).show()
}
override fun onDataChange(datap0: DataSnapshot?) {
try {
var td = datap0!!.value as HashMap<String,Any>
MyList.clear()
for (key in td.keys){
var data = td[key] as HashMap<String,Any>
MyList.add(ForList(
data["pic"] as String
,data["txt1"] as String
,data["txt2"] as String
,data["m1"] as String
,data["m2"] as String
,data["m3"] as String
)
)
adapter!!.notifyDataSetChanged()
}
}catch (ex:Exception){
Log.e("MyAPP","Error on you app 0000",ex)
}
}
})
}
You're not asking for the data in any specific order: mRef.child("MyPosts").child("TEST").addValueEventListener(.... If you want the data in a specific order, you will have to specify that. For example, to get the child nodes in the order of their keys:
mRef.child("MyPosts").child("TEST").orderByChild().addValueEventListener(...
With this the DataSnapshot that is passed into onDataChange will contain the data in the order of the keys. But if you then get the value like this datap0!!.value as HashMap<String,Any> you are dropping any ordering information, since a map can't maintain the information about the ordering of the child nodes.
To process the data in the order you requested you should look over the child nodes of the DataSnapshot with:
override fun onDataChange(snapshot: DataSnapshot?) {
MyList.clear()
for (child: DataSnapshot? in snapshot.getChildren()) {
var data = child!!.value as HashMap<String,Any>
...

Categories

Resources