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.
Related
There are multiple RecyclerView in my application. Each one consists of the same records, but with different filters.
For example, the first RecyclerView contains new records, the second RecyclerView contains the most popular, etc.
I am trying to get "voices" with different filters. But in the end I get 2 identical lists.
My ViewModel:
private var recentlyAddedVoices = MutableLiveData<List<VoicesModel>>()
private val topFreeVoices = MutableLiveData<List<VoicesModel>>()
private val favExists = MutableLiveData<Boolean>()
private val addToFavoriteResult = MutableLiveData<Boolean>()
val homeVoicesData: MutableLiveData<Pair<List<VoicesModel>?, List<VoicesModel>?>> =
object: MediatorLiveData<Pair<List<VoicesModel>?, List<VoicesModel>?>>() {
var voices: List<VoicesModel>? = null
var freeVoices: List<VoicesModel>? = null
init {
addSource(recentlyAddedVoices) { voices ->
this.voices = voices
voices?.let { value = voices to it }
}
addSource(topFreeVoices) { free ->
this.freeVoices = free
freeVoices?.let { value = freeVoices to it }
}
}
}
fun loadRecentlyAddedVoices(){
REF_DATABASE_ROOT.child(NODE_STICKERS).addValueEventListener(object :
ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val tmpList: MutableList<VoicesModel> = mutableListOf()
for (ds in snapshot.children) {
val voices: VoicesModel? = ds.getValue(VoicesModel::class.java)
voices!!.pushKey = ds.key.toString()
tmpList.add(voices)
}
recentlyAddedVoices.postValue(tmpList)
}
override fun onCancelled(error: DatabaseError) {
}
})
}
fun loadTopFree(){
REF_DATABASE_ROOT.child(NODE_STICKERS).
orderByChild(CHILD_IS_FREE).
equalTo(true).
addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val tmpList: MutableList<VoicesModel> = mutableListOf()
for (ds in snapshot.children) {
val voices: VoicesModel? = ds.getValue(VoicesModel::class.java)
voices!!.pushKey = ds.key.toString()
tmpList.add(voices)
}
topFreeVoices.postValue(tmpList)
}
override fun onCancelled(error: DatabaseError) {
}
})
}
Observe in Fragment:
firebaseViewModel.homeVoicesData.observe(this){ (recentlyAdded, topFree) ->
// recentlyAdded and topFree equals identical value
UpdateUI()
}
I am getting issues of getting my USER KEY and it returned as null even if there's a username.
Thing is I am just trying to get my username.
I am currently using firebase database
class NewMessageActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_new_message)
supportActionBar?.title="Select User"
/*
val adapter = GroupAdapter<ViewHolder>()
adapter.add(UserItem())
adapter.add(UserItem())
adapter.add(UserItem())
new_message_list.adapter = adapter
*/
fetchusers()
}
companion object {
val USER_KEY = "USER_KEY"
}
private fun fetchusers(){
val ref = FirebaseDatabase.getInstance().getReference("/users")
ref.addListenerForSingleValueEvent(object: ValueEventListener {
override fun onDataChange(p0: DataSnapshot) {
val adapter = GroupAdapter<ViewHolder>()
p0.children.forEach {
Log.d("NewMessage", it.toString())
val user = it.getValue(User::class.java)
if (user != null){
adapter.add(UserItem(user))
}
}
adapter.setOnItemClickListener { item, view ->
val userItem = item as UserItem
val intent = Intent(view.context, ChatLogActivity::class.java)
intent.putExtra(USER_KEY, userItem.user.username)
startActivity(intent)
finish()
}
new_message_list.adapter = adapter
}
override fun onCancelled(p0: DatabaseError) {
}
})
}
}
class UserItem(val user: User): Item<ViewHolder>() {
override fun bind(viewHolder: ViewHolder, position: Int){
//list stuff
viewHolder.itemView.username_textview_new.text = user.username
Picasso.get().load(user.profileImageUrl).into(viewHolder.itemView.imageview_new_msg)
}
override fun getLayout(): Int {
return R.layout.user_row_new_message
}
}
This one really frustrated me for hours. I needed this for my chat log title for each person
Maybe I should skip this?
I am just new to android development
Can anyone help?
error in debug
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.
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}")
}
}
}