I'm working on an application in kotlin for sending an image to a random user or to multiple user from firebase. My database is structured like this :
Users
bVk3KBL81AY2LUVUbC6G1qcLAcl2
username : test
uid : bVk3KBL81AY2LUVUbC6G1qcLAcl2
And I don't know how to get a random user from firebase, I searched but I don't find the best solution for my structure.
I try with this post : How to get unique random product in node Firebase?
But the code doesn't work I have multiple errors
Thank you in advance to any one who may be able to give me some solutions.
Edit #1
I've worked on it and it's working, but I don't know how limit the random to 3 users for example.
In my application the user can change the limit of user so I need to change the random limit.
My code :
fun chooseRandomUser() {
val rootRef = FirebaseDatabase.getInstance().getReference()
val productsRef = rootRef.child("users")
val valueEventListener: ValueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val productList = ArrayList<String?>()
for (ds in dataSnapshot.children) {
val name = ds.child("username").getValue(String::class.java)
productList.add(name)
Log.d("FragmentActivity", "usernames : $name")
}
val productListSize = productList.size
val randomProductList = ArrayList<kotlin.Int>()
randomProductList.add(Random().nextInt(productListSize))
Log.d("FragmentActivity", "list : $randomProductList")
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("FragmentActivity", "Error: ${databaseError.message}")
}
}
productsRef.addListenerForSingleValueEvent(valueEventListener)
}
As far as I could understand, you want to pick a random user from all the list of users that you have in your firebase database. You can simply do that on the android side.
I think you already know how to get all the users from the firebase database. Store the users in a list. Then I would try to come up with a random function which could give me an index of the list randomly and I could pick that user from the list based on that random index.
The same implementation can go for selecting multiple users from that list as well.
I hope that helps.
Related
I am having a hard time to solve a problem. So basically I am trying to migrate my database from Room in Android to Firebase. I have been able to store my values in Firebase following a similar structure I was trying to save in Room Database.
Now the main issue which I am facing is while retrieving the values from Firebase. Being more specifically I am working with nested recycler views so I have a bit complex structure. I will explain it below.
So the data works like, there are floors and for each floor there are rooms and for each room there are machines. So it goes in that hierarchy. When I was working with local database I created a function which handles this functionality in my ViewModel :
This is how it looks :
fun load() {
//Observing all floors
getAllFloors.observeForever(Observer {
viewModelScope.launch(Dispatchers.Main) {
/** Converting list of floors to a distinct and sorted floor list
* Input -> [0,0,1,2,3,4,2,4,1,3], Output -> [0,1,2,3,4]
*/
val distinctFloorNames = it.distinct().sorted()
val floorsList = mutableListOf<FloorsDataClass>()
val devicesList = mutableListOf<String>()
//Loop over distinct floors for getting each floor
for (floorName in distinctFloorNames) {
//At each floor prepare a list of rooms
val rooms = repository.getAllRooms(floorName)
//Getting distinct (in case rooms gets repeated -> only during testing) and sorted rooms
val distinctRoomNames = rooms.distinct().sorted()
Timber.d("Floor: $floorName, Rooms: $distinctFloorNames")
val roomsList = mutableListOf<RoomsDataClass>()
//Loop over rooms in the floor
for (roomName in distinctRoomNames) {
//In each room prepare a list of devices
val devicesName = repository.getAllDevices(roomName)
val distinctDeviceName = devicesName.distinct().sorted()
//Transform the list of string to list of DeviceClassObject
val deviceData = mutableListOf<DevicesDataClass>()
//For each device get the attached machine
for (device in distinctDeviceName) {
//Get the machine associated with the device
val machine = repository.getMachine(device)
Timber.d("Machine: $machine")
//Attach the device and machine to the [DevicesDataClass Object]
deviceData.add(DevicesDataClass(device, machine))
/**Attach the room name and the devices list to the
*[RoomDataClass Object]
**/
roomsList.add(RoomsDataClass(roomName, deviceData))
//Saving devices in a list for managing
devicesList.add(device)
}
}
/**Add the room list to the floor object and
add the floor to the floor list **/
floorsList.add(FloorsDataClass(floorName, roomsList))
}
//Sending the list as livedata to be further observed - from add details for device - manage devices fragment
devicesLiveData.postValue(devicesList)
/** Post the complete value of floorList in the floorListLiveData which will be
* observed from the [ControlPanelFragment]
*/
floorListLiveData.postValue(floorsList)
Timber.d("$floorsList")
}
})
}
Now to display the data I just observed this floorsList and then pass it to my nested adapters which shows data accordingly.
I am trying to fetch data from Firebase in a similar way. I have reached to a point where I am even able to fetch my floors and rooms for each floor but the problem comes while fetching the machines.
Basically I am using two ValueEventListener in my project. I am using the value coming from one of the listeners to fill my data. But as reading data from Firebase is asynchronous my data field shows empty because I try to use that data before it comes from the database. That's like the main issue.
Firebase structure
Code for reading values from Firebase
private fun readRoomsAndFloorFromFirebase(): List<FloorsDataClass> {
val roomsDataClass: MutableList<RoomsDataClass> = mutableListOf()
val devicesDataClass: MutableList<DevicesDataClass> = mutableListOf()
val floorsDataClass: MutableList<FloorsDataClass> = mutableListOf()
val listener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
var floors: FloorsDataClass
// Log.d(TAG, "Data: ${snapshot}")
for (i in snapshot.children) {
Log.i(TAG, "Data: $i")
// floor = "${i.key}"
for (j in i.children) {
Log.i(TAG, "Value: ${j.key}")
// roomsList.add("${j.key}")
val listener = object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
// Log.w(TAG, "Listener: ${snapshot.child("Device ID").value}")
val device = snapshot.child("Device ID").value.toString()
val machine = snapshot.child("Machine").value.toString()
devicesDataClass.add(DevicesDataClass(device, machine))
}
override fun onCancelled(error: DatabaseError) {}
}
//Getting the list of devices and saving it with particular room
roomsDataClass.add(RoomsDataClass("${j.key}", devicesDataClass))
realtime.child("USERS").child(auth.uid!!).child(
"ADDED DEVICES"
).child("${i.key}").child("${j.key}")
.addValueEventListener(listener)
}
//Storing the particular floor with room data class values
floors = FloorsDataClass("${i.key}", roomsDataClass)
floorsDataClass.add(floors)
}
Log.e(TAG, "List 1: $floorsDataClass")
}
override fun onCancelled(error: DatabaseError) {}
}
realtime.child("USERS").child(auth.uid!!).child("ADDED DEVICES")
.addValueEventListener(listener)
Log.e(TAG, "List: $floorsDataClass")
return floorsDataClass
}
Data Classes :
data class FloorsDataClass(val floor: String, val rooms: List<RoomsDataClass>)
data class RoomsDataClass(val room:String, val devices: List<DevicesDataClass>)
data class DevicesDataClass(val device: String, val machine: String?)
P.S - I want to read data from that firebase structure such that I have one object which contains the first element as the first floor then inside it, it can store rooms and then further it can store devices for that room. Once the room loop is done, I want to go ahead and save that with the floor.
If more code or ss required to understand the question please comment.
When i try get user information by his uid which is same like in firebase database this error is shown.
uid is passed in right way I have checked it. Route to database is good and I have checked this too.
Here is my code and this probably don't have any mistakes because everything work and only Firebase had some problems.
class Profil : AppCompatActivity() {
private var fAuth: FirebaseAuth?=null
private var database: DatabaseReference?=null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_profil)
fAuth= FirebaseAuth.getInstance()
// ✋ Here I checked with uid and uid.toString()
var userCheck = fAuth!!.currentUser?.uid.toString()
getData(userCheck)
// ✋ Here I checked that good uid is get
//tv_firsName.text = userCheck
}
private fun getData(userCheck: String) {
database = FirebaseDatabase.getInstance().getReference("Users")
database!!.child(userCheck).get().addOnSuccessListener{
if(it.exists()){
val firstName = it.child("firstName").value
val lastName = it.child("lastName").value
val position = it.child("position").value
val email = it.child("email").value
tv_Imie.text = firstName.toString()
tv_Nazwisko.text = lastName.toString()
tv_Stanowisko.text = position.toString()
tv_Email.text = email.toString()
}else{
Toast.makeText(this, "User does not exist", Toast.LENGTH_SHORT).show()
}
}.addOnFailureListener{
Toast.makeText(this, "Failed", Toast.LENGTH_SHORT).show()
Log.e("firebase", "Error getting data", it)
}
}
}
Here is my database structure
I checked some ways to solve this problem.
I have seen some people propose a solution with addValueEventListener, but then the list is fetched. I want single user information to be included in my textViews. I read that the problem may be related to a bug in Firebase and not in the code.
Ok, problem was with Firebase not my application. I don't know why but when I started app in every try Firebase closed my connection with database. I tried add to google.services.json some links to database and change some settings but this didn't work. After all I removed app from firebase and delete file google.services.json. I added app again on website then I connect app with firebase and new google.services.json was set. That's all everything is working thanks for help.
I am trying to fetch data from firebase based on userId, when the user logged in I need to show his profile details based on its id but I am unable to fetch it. Any help?
auth = FirebaseAuth.getInstance()
databaseRef = FirebaseDatabase.getInstance().getReference("Users/")
databaseRef.addValueEventListener(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
//I want first_name and all other details of users
Log.e("first_name", snapshot.child(uid).getValue(String::class.java))
}
override fun onCancelled(error: DatabaseError) {
TODO("Not yet implemented")
}
})
One problem is that you don't seem to initialize uid anywhere. So that should be:
auth = FirebaseAuth.getInstance()
uid = auth.currentUser.uid
Also see the Firebase documentation on getting the current user.
Next is not necessarily a bug, but definitely an optimization you'll want to do. Right now you read the entire Users node into your Android app, to then only get a value from /Users/$uid. As you add more users to the database, this is not going to scale.
What you'll want to do instead, is only load the node for the current user from the database with:
databaseRef = FirebaseDatabase.getInstance().getReference("Users/")
var userRef = databaseRef.child(uid)
userRef.addValueEventListener(object: ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
Log.e("first_name", snapshot.getValue(String::class.java))
}
...
Ok, so this is what my Fireabse database looks like:
Now I need to update the "about" node for every user. Without the user node, this is what I would do:
val mAllUserDatabase : DatabaseReference = FirebaseDatabase.getInstance().reference.child("Users"))
val childUpdates = HashMap<String, Any>()
childUpdates["/about"] = "new value"
mAllUserDatabase.updateChildren(childUpdates)
The problem is, I need to define the node of the user (don't I?). But since the user node all have a different name, I would need something like "any- node" or "all nodes". Something like
childUpdates["/any/about"] = "new value"
or getChildren()of the snapshots.
What would be the best way to solve this? Do I have to use addListenerForSingleValueEvent to read the names of all nodes and then use the names on mAllUserDatabase with .updateChildren(childUpdates)? That cant be the best way to do this.
To update your about property within all user objects, please use the following lines of code:
val rootRef = FirebaseDatabase.getInstance().reference
val usersRef = rootRef.child("Users")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val childUpdates = HashMap<String, Any>()
childUpdates["about"] = "yourNewAboutValue"
ds.ref.updateChildren(childUpdates)
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
usersRef.addListenerForSingleValueEvent(valueEventListener)
To write a value to a node in the Firebase Realtime Database you need to know the exact path to that node. Firebase does not have the concept of SQL's update queries.
So in your case you'll need to first read the nodes you want to update, then loop over them, and then update them either one by one or with one big multi-location update statement.
I'm using Firebase real-time database in my Android project. My database scheme as below :
I want to get best 10 scores. My query :
val query = reference.child("users").orderByChild("score").limitToFirst(10)
query.addValueEventListener(object : ValueEventListener {
override fun onCancelled(p0: DatabaseError) {}
override fun onDataChange(dataSnapshot: DataSnapshot) {
dataSnapshot.children.forEach {
val score: String = it.child(Constants.scoreKey).value as String
Log.i(TAG,"score:"+score)
}
}
})
I hope to see best 10 scores on logcat. But it didn't. I see random scores.
What is my mistake ?
In order to solve this, change the type of your score field from String to int or long, otherwise the order is done lexicographically.
There is no operator in Firebase and as far as i know nor in other databases that allow you to change this behavior. Instead, you will need to modify the data to get the behavior you want. If you want to go with the String value, store the values that are in the order you need them when sorted lexicographically. For numbers you can accomplish that by padding them with zeroes:
As an example:
"0131" //zero added before
"0132" //zero added before
......
"1308"
"1309"
"1310"
"1311"