I'm retrieving a node from Database that looks like this:
DataSnapshot { key = appointment, value = {timeAppointment=12:00pm, dateAppointment=12/12/1221, clientUID=Bu4sw8ouUUhDv0Ut1IKqeh8kESg2, caseManagerName=Karla Moreno, userName=Sakura Nakamura, formatAppointment=Zoom} }
This DataSnapshot is being stored in a custom callback.
However, I'm only able to retrieve it as a DataSnapshot. I'd like to retrieve the values, and display them using a TextView.
Is there a way to convert it into a HashMap, other than hardcoding the values into a HashMap?
Here is my code:
class ViewAppointments : AppCompatActivity() {
var readAppointment: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_view_appointments)
readData(object : AppointmentCallback {
override fun onCallback(value: DataSnapshot) {
//Here readAppointment is of type DataSnapshot
readAppointment = value.toString()
Log.d("ZXC", "$readAppointment")
}
})
displayData()
}
private fun displayData() {
// returns null
Log.d("ZXC", "Display appointment from Database: $readAppointment")
}
private fun readData(appointmentCallback: AppointmentCallback) {
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val rootRef = FirebaseDatabase.getInstance().reference
val uidRef = rootRef.child("users").child(uid)
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (item in dataSnapshot.children) {
if (item.key.toString() == "appointment") {
//item = DataSnapshot { key = appointment, value = {timeAppointment=12:00pm, dateAppointment=12/12/1221,
// clientUID=Bu4sw8ouUUhDv0Ut1IKqeh8kESg2, caseManagerName=Karla Moreno, userName=Sakura Nakamura,
// formatAppointment=Zoom} }
Log.d("ZXC", "$item")
appointmentCallback.onCallback(item)
}
}
}
override fun onCancelled(databaseError: DatabaseError) {
}
}
uidRef.addListenerForSingleValueEvent(valueEventListener)
}
interface AppointmentCallback {
fun onCallback(value: DataSnapshot)
}
}
To get the map of values in a snapshot, call its getValue() method. This might actually be mapped to a value property in Kotlin.
Related
I want to get the upper child ID when the current user is equal to user ID in the child
and i have this
could you help me ?
fun loadOwnOffer() {
databaseReference.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (data in snapshot.children) {
val model = data.getValue(OffersModelClass::class.java)
val userID: String = model?.userID.toString()
if (userID == firebaseAuth.currentUser?.uid) {
Log.d("Getting User ID", userID)
}
}
}
}
If you want to get the offers for a specific UID, you can do that with a query:
fun loadOwnOffer() {
asser(Firebase.auth.currentUser != null) { "Cannot load offer without active user" }
val myUid = Firebase.auth.currentUser.uid
val database = Firebase.database
val offersRef = database.getReference("offers")
val myOffersQuery = offers.orderByChild("userID").equalTo(myUid)
myOffersQuery.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (data in snapshot.children) {
...
So I use a ViewModel and LiveData for reading data from my Firebase Realtime Database.
This is my ViewModel:
class UsersViewModel : ViewModel() {
private val uid = Firebase.auth.currentUser!!.uid
private val USERS_REF: DatabaseReference = FirebaseDatabase.getInstance().getReference("/users/$uid")
private val liveData: FirebaseQueryLiveData = FirebaseQueryLiveData(USERS_REF)
private val usersLiveData: MediatorLiveData<Users> = MediatorLiveData()
init {
usersLiveData.addSource(liveData, object : Observer<DataSnapshot> {
override fun onChanged(dataSnapshot: DataSnapshot?) {
if (dataSnapshot != null) {
usersLiveData.postValue(dataSnapshot.getValue(Users::class.java))
} else {
usersLiveData.value = null
}
}
})
}
#NonNull
fun getUsersLiveData() : LiveData<Users> {
return usersLiveData
}
}
This is my LiveData:
class FirebaseQueryLiveData(ref: DatabaseReference) : LiveData<DataSnapshot>() {
private val query: Query = ref
private val listener: MyValueEventListener = MyValueEventListener()
private var listenerRemovePending = false
private val removeListener = object : Runnable {
override fun run() {
query.removeEventListener(listener)
listenerRemovePending = false
}
}
override fun onActive() {
super.onActive()
if (listenerRemovePending) {
Handler(Looper.getMainLooper()).removeCallbacks(removeListener)
} else {
query.addValueEventListener(listener)
}
listenerRemovePending = false
}
override fun onInactive() {
super.onInactive()
Handler(Looper.getMainLooper()).postDelayed(removeListener, 2000)
query.removeEventListener(listener)
}
private inner class MyValueEventListener : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
value = snapshot
}
override fun onCancelled(error: DatabaseError) {
return
}
}
}
How do I do delete data from my database? I know I need to call removeValue() on a database reference, but how I should go about doing that? I have the database reference in my ViewModel so I would like to use that reference for deleting data.
As per MVVM, you can have a repository class with delete method which you can call from your viewmodel class. Then you can use livedata to be observed by the view for progress changes.
To delete:
val databaseReference = FirebaseDatabase.getInstance().getReference().child("quotes").child(fieldKey)
databaseReference.removeValue()
I wanted to make button visibe only for users with uid from Admins node, but somehow it doesn't work. That's the function for this(uid value is setting earlier and it is the uid of current user):
private fun checkAdmin() {
val ref = FirebaseDatabase.getInstance().getReference("/admins/")
ref.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onCancelled(p0: DatabaseError) { }
override fun onDataChange(p0: DataSnapshot) {
if (p0.exists()) {
if (uid == p0.value.toString()) {
createNewButton.visibility = View.VISIBLE
} else {
createNewButton.isEnabled = false
createNewButton.visibility = View.INVISIBLE
}
}
}
})
}
There is the part from JSON file:
"admins" : [ "rTXdtJsE7qPZRpWnwTGBAX7dIxx1","4kwOjCjkKvazfoMcZygfsn1byB72" ]
A quick solution for your problem might be the following code:
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val rootRef = FirebaseDatabase.getInstance().reference
val adminsRef = rootRef.child("admins")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val value = ds.getValue(String::class.java)
if(value.equals(uid)) {
createNewButton.visibility = View.VISIBLE
} else {
createNewButton.isEnabled = false
createNewButton.visibility = View.INVISIBLE
}
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore errors!
}
}
adminsRef.addListenerForSingleValueEvent(valueEventListener)
To get those values, you need to loop through the DataSnapshot object.
How i can optimize my code?
In every function i created valueEventListener.
Here is all code:
class TargetsPresenter(private val contract: SelectTargetViewContract) {
var firebaseUser: FirebaseUser? = null
var targetList: ArrayList<Goal> = ArrayList()
private var databaseReference: DatabaseReference? = null
private var targetsRef: DatabaseReference? = null
private var uid: String? = null
fun setInitialData() {
firebaseUser = FirebaseAuth.getInstance().currentUser
databaseReference = FirebaseDatabase.getInstance().reference
uid = firebaseUser?.uid
targetsRef = databaseReference?.child("targets")
?.child("users")?.child(uid.toString())
?.child("targets")
}
fun getTargetsFromDb() {
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
targetList.clear()
dataSnapshot.children
.mapNotNull { it.getValue(Goal::class.java) }
.toCollection(targetList)
contract.updateViewContent()
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("some", "Error trying to get targets for ${databaseError.message}")
}
}
targetsRef?.addListenerForSingleValueEvent(valueEventListener)
}
fun getTargetsByPriority() {
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
targetList.clear()
dataSnapshot.children
.mapNotNull { it.getValue(Goal::class.java) }
.sortedBy { it.priority }
.toCollection(targetList)
contract.updateViewContent()
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("some", "Error trying to get targets for ${databaseError.message}")
}
}
targetsRef?.addListenerForSingleValueEvent(valueEventListener)
}
fun getTargetsByDeadline() {
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
targetList.clear()
dataSnapshot.children
.mapNotNull { it.getValue(Goal::class.java) }
.sortedBy { it.deadline }
.toCollection(targetList)
contract.updateViewContent()
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("some", "Error trying to get targets for ${databaseError.message}")
}
}
targetsRef?.addListenerForSingleValueEvent(valueEventListener)
}
}
Optimization is the wrong word to describe the issue. The issue is repeating identical code (violating the DRY principle), which can be a problem because it invites error if you need to change something, and it's less readable.
In this case, it's not extreme, but I guess it could be improved somewhat. You can declare a class implementation of the listener that takes a parameter for how to sort the list.
class TargetsPresenter(private val contract: SelectTargetViewContract) {
//...
fun getTargetsFromDb() {
targetsRef?.addListenerForSingleValueEvent(MyValueEventListener<String>())
}
fun getTargetsByPriority() {
targetsRef?.addListenerForSingleValueEvent(MyValueEventListener(Goal::priority))
}
fun getTargetsByDeadline() {
targetsRef?.addListenerForSingleValueEvent(MyValueEventListener(Goal::deadline))
}
private inner class MyValueEventListener<R: Comparable<R>>(
private val sortCriteria: (Goal) -> R? = { null }
) : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
targetList.clear()
dataSnapshot.children
.mapNotNull { it.getValue(Goal::class.java) }
.sortedBy(sortCriteria)
.toCollection(targetList)
contract.updateViewContent()
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("some", "Error trying to get targets for ${databaseError.message}")
}
}
}
I add targets to the database this way:
private fun addTarget(name: String, description: String) {
if (!TextUtils.isEmpty(name)) {
val target = Target(guid = "some", name = name, description = description)
databaseReference?.child("users")
?.child(mUserId.toString())?.child("targets")?.push()?.setValue(target)
} else Log.d("some", "Enter a name")
}
And get the following structure in my firebase database:
Next, I try to display my list of targets in TargetsFragment
In onViewCreated i call next functions:
private fun updateListData() {
databaseReference = FirebaseDatabase.getInstance().getReference()
getTargetsFromDb()
}
private fun getTargetsFromDb() {
databaseReference?.child("users")?.child(mUserId.toString())?.
child("targets")?.addValueEventListener(object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (targetSnapshot in dataSnapshot.children) {
val target = targetSnapshot.getValue(Target::class.java)
target?.let { targetList.add(it) }
}
recyclerView?.adapter = adapter
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("some", "Error trying to get targets for ${databaseError.message}")
}
})
}
As I said, because I cannot see what changes do you make, I wrote the code that can help you get the data from the database:
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val rootRef = FirebaseDatabase.getInstance().reference
val targetsRef = rootRef!!.child("targets").child("users").child(uid).child("targets")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val target = ds.getValue(Target::class.java)
targetList.add(target)
}
adapter.notifyDataSetChanged()
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
targetsRef.addListenerForSingleValueEvent(valueEventListener)
The output in the logcat will be:
uuuuu
yyyyy
Even if you are using two nodes with the same name targets, both should be mentioned in the reference.