Extract Data from firebase - android

Unable to extract information from the datasnapshot received from firebase.
Currently, I am able to get the dataSnapshot from firebase, but I am having problems extracting the information from it.
In the example below I have a lobby with the code "81MUB" and inside I have a list of players (only using one player in the example). Data from FireBase
{
"81MUB": [
{
"name": "Alejandro",
"points": 0
}
]
}
Data Class
data class Player(
val name: String,
val points: Int
)
Listener
fun getCode(): String {
val index = ('A'..'Z') + ('1'..'9')
var code = ""
for (i in 0..4){
code += index[Random().nextInt(index.size)]
}
return code
}
class MviewModel : ViewModel() {
private val _Players: MutableLiveData<MutableList<Player>> =
MutableLiveData(mutableListOf<Player>(Player("Alejandro", 0)))
private var _LobbyCode: String = ""
private val dataBase = FirebaseDatabase.getInstance()
fun getPlayer(): MutableLiveData<MutableList<Player>> = _Players
fun createLobby() {
_LobbyCode = getCode()
}
fun listener() {
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
}
override fun onCancelled(databaseError: DatabaseError) {
// Getting Post failed, log a message
}
}
dataBase.reference.child(_LobbyCode).addValueEventListener(postListener)
}
}
Any tips?

Each time you call getCode() you are generating a new random code. When reading data, you always use the exact same code that exists in the database. So in code, it should look like this:
val db = Firebase.database.reference
val codeRef = db.child("81MUB")
codeRef.get().addOnCompleteListener {
if (it.isSuccessful) {
val snapshot = it.result
val name = snapshot.child("name").getValue(String::class.java)
val points = snapshot.child("points").getValue(Long::class.java)
Log.d("TAG", "$name/$points")
} else {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
The result in the logcat will be:
Alejandro/0
If you however want to map the 81MUB node into an object of type Player, then your data class should look like this:
data class Player(
val name: String? = null,
val points: Int? = null
)
And in code:
val db = Firebase.database.reference
val codeRef = db.child("81MUB")
codeRef.get().addOnCompleteListener {
if (it.isSuccessful) {
val snapshot = it.result
val player = snapshot.getValue(Player::class.java)
Log.d("TAG", "${player.name}/${player.points}")
} else {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
Which will produce the exact same output as above.
You might also take into consideration, using the DatabaseReference#push() method which:
Create a reference to an auto-generated child location. The child key is generated client-side and incorporates an estimate of the server's time for sorting purposes.
Instead of using your codes.

Related

Kotlin Firebase foreach child in path

I have a simple to-do app in Kotlin and I want to get data from "task" node in firebase on app startup. For each child, I want to create a Todo object.
var todo = Todo("child data here")
Getting specific task
val database = FirebaseDatabase.getInstance()
val ref = database.getReference("task")
var todo = ref.child("task1").key?.let { Todo(it) }
if (todo != null) {
todoAdapter.addTodo(todo)
}
I want to get all children, there can be more than three.
If you want to get all children of a particular node, no matter how many are actually present there, then you should loop over that node using getChildren() method, as you can see in the following lines of code:
val db = FirebaseDatabase.getInstance().reference
val taskRef = db.child("task")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val value = ds.getValue(String::class.java)
Log.d("TAG", value)
//Create the desired object
var todo = Todo(value) //👈
}
}
override fun onCancelled(error: DatabaseError) {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
}
taskRef.addListenerForSingleValueEvent(valueEventListener)
The result in the logcat will be:
task1
task2
task3
.....

Can't add Firebase document Id to dataClass

I have a data class for data that come from user entries. Ä°t is carrying this data to Firebase. This data class also includes documentId variable which is a empty string by default. I want to add document Id's that Firebase created automatically. I tried every way I could think of. But it takes default value in any way.
Here are the four code snippets about this issue. Data class, adding data activity, and retrieving data activity and their View Models.
Dataclass:
data class AnalyzeModel(
var concept: String?="",
var reason: String?="",
var result: String?="",
var rrRatio: Double?=0.0,
var tarih: Timestamp=Timestamp.now(),
var tradingViewUrl: String="",
var id : String="")
AddAnalyzeActivity, addData function:
fun addData(view: View) {
val tarih = com.google.firebase.Timestamp.now()
val rr = rrText.text.toString()
var doubleRR = rr.toDoubleOrNull()
if (doubleRR == null) { doubleRR = 0.0 }
val analyzeDTO = AnalyzeModel(
conceptText.text.toString(),
reasonForText.text.toString(),
resultAddingText.text.toString(),
doubleRR,
tarih,
chartImage.text.toString()
)
viewModel.save(analyzeDTO)
val intent = Intent(this, PairDetailActivity::class.java)
startActivity(intent)
finish()
}
AddAnalyze ViewModel, save function:
fun save(data: AnalyzeModel) {
database.collection(dbCollection!!).document("Specified").collection("Pairs")
.document(chosenPair!!)
.collection("Analysis")
.add(data)
.addOnFailureListener { exception ->
exception.printStackTrace()
Toast.makeText(getApplication(), exception.localizedMessage, Toast.LENGTH_LONG).show()
}
}
PairViewModel, retrieveData function:
private fun retrieveData() {
val docRef = collectionRef.orderBy("tarih", Query.Direction.DESCENDING)
docRef.addSnapshotListener { value, error ->
try {
if (value != null && !value.isEmpty) {
val allAnalysis= ArrayList<AnalyzeModel>()
val documents = value.documents
documents.forEach {
val analyze = it.toObject(AnalyzeModel::class.java)
if (analyze!=null){
allAnalysis.add(analyze)
}
}
list.value = allAnalysis
} else if (error != null) {
Toast.makeText(Application(), error.localizedMessage, Toast.LENGTH_LONG).show()
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
I want to add document IDs that Firebase created automatically.
To solve this, you only need to annotate the field with #DocumentId.
data class AnalyzeModel(
var concept: String?="",
var reason: String?="",
var result: String?="",
var rrRatio: Double?=0.0,
var tarih: Timestamp=Timestamp.now(),
var tradingViewUrl: String="",
#DocumentId 👈
var id : String=""
)
Be also sure to have the latest version of Firestore.

FireBase multiple queries by document and collection

I am struggling with firebase to run one query to take the truckDocumentId and after that to run another query to take the routesByDateDocumentIdand at the end I am using both document ids to run the function "sendGpsPosition", my problem is that the first query finds truckDocumentId but sometimes the second query does not execute and that is why the applications stops. The code below is for Kotlin.
If I am on Debug then most of the time works.. if I switch off the debug it almost shows the error below =>
And because the query does not execute I got this error: java.lang.IllegalArgumentException: Invalid document reference. Document references must have an even number of segments, but trucks has 1
suspend fun getTruckId() {
val trucksReference = firestore.collection("trucks").whereEqualTo("dispatcher", "Miro")
.whereEqualTo("name", "PEUGEOT").get().await()
val document = trucksReference.documents[0]
if (document != null) {
truckDocumentId = document.id
}
}
suspend fun getRouteReferenceId() {
val routesByDate = firestore.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date").get().await()
val documentRoute = routesByDate.documents[0]
if (documentRoute != null) {
routesByDateDocumentId = documentRoute.id
}
}
fun sendGpsPosition(lat: Double, long: Double, imageRef: String? = null) {
runBlocking { getTruckId() } // if I get this DocumentID
runBlocking { getRouteReferenceId() } // this here maybe will be not found or maybe will be found.. the async is not done correct not sure how to do it.
firestore
.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.document(routesByDateDocumentId)
.collection("live_route")
.add(LatLong(Timestamp.now(), lat, long))
}
**I solved it this way.**
private suspend fun getTruckId() {
val trucksReference = firestore.collection("trucks")
.whereEqualTo("dispatcher", "Miro")
.whereEqualTo("name", "VW")
.get()
.await()
val document = trucksReference.documents[0]
if (document != null) {
truckDocumentId = document.id
}
}
private suspend fun getRouteReferenceId() {
val currentTime = Timestamp.now()
val routesByDate = firestore.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.get()
.await() // here will be better to look for data by delivery_day
val documentRoute = routesByDate.documents[0]
if (documentRoute != null) {
routesByDateDocumentId = documentRoute.documents[0].id
}
}
private fun addGpsDataInDatabase(lat: Double, long: Double, imageRef: String? = null) {
firestore
.collection("trucks")
.document(truckDocumentId)
.collection("routes_by_date")
.document(routesByDateDocumentId)
.collection("planned_route") //planned_route or live_route depends if we want to show current state of a truck of make a route plan
.add(LatLong(Timestamp.now(), lat, long))
}
fun sendGpsPosition(lat: Double, long: Double, imageRef: String? = null) {
GlobalScope.launch {
val truckDocId = async { getTruckId() }
truckDocId.await()
val routeDocId = async { getRouteReferenceId() }
routeDocId.await()
addGpsDataInDatabase(lat, long, imageRef)
}
}

How to propery listen to child changes in FirebaseDatabase

I am trying to listen to my database child changes, in my case it is orders.
Below is a picture of my database, where
ODdPag... Is the uid of my customer
Lu-_1A is just .push random order naming
I can provide my code but it is confusing and not working, because i can only access Orders and not its next child.
Now I want to list all my orders and lister for changes in sub children (in order names) not uid.
I am using:
val db = FirebaseDatabase.getInstance.reference
val ref = db.child("/Orders/")
ref.addChildEventListener(object: ChildEventListener {
override fun onChildAdded(p0: DataSnapshot, p1: String?) {
ringtone.play()
itemListTable.clear()
p0.children.forEach {
it.child("order").children.forEach{ item ->
val newData = item.getValue(itemListData::class.java) ?: return
newData.itemName = item.key!!
newData.orderKey = it.key!!
itemListTable.add(newData)
}
val data = it.getValue(itemRowData::class.java) ?: return
adapter.add(itemRow(data.phoneNumber,data.time,data.locationLat,data.locationLong,data.optionalAddress,data.optionalNote,data.totalPrice,itemListTable,it.key))
}
}
override fun onChildChanged(p0: DataSnapshot, p1: String?) {
Log.d("ac1234","$p0")
p0.child("order").children.forEach{ item ->
Log.d("ac1234","1")
val newData = item.getValue(itemListData::class.java) ?: return
Log.d("ac1234","2")
newData.itemName = item.key!!
newData.orderKey = p0.key!!
itemListTable.add(newData)}
val data = p0.getValue(itemRowData::class.java) ?: return
adapter.add(itemRow(data.phoneNumber,data.time,data.locationLat,data.locationLong,data.optionalAddress,data.optionalNote,data.totalPrice,itemListTable,p0.key))
}
class itemRowData(val phoneNumber :String = "",val time :String = "",val locationLat :Double = 0.0,val locationLong :Double = 0.0,val optionalAddress :String = "",val optionalNote :String = "",val totalPrice :String = "")
class itemListData(var itemName: String = "" ,val totalQuantity: String = "",val totalPrice :Long = 0,var orderKey :String = "")
Logcat: 1 and 2 are not called
P0 shows 4 rows full of all data every time I send an order
I want all orders from all users, that is my problem. I cant reference to every ("/Orders/uid") to listen to child changes
To get all orders of all users, you should use a ValueEventListener, like in the following lines of code:
val rootRef = FirebaseDatabase.getInstance().reference
val ordersRef = rootRef.child("Orders")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (uidDataSnapshot in dataSnapshot.children) {
for (orderDataSnapshot in uidDataSnapshot.children) {
val newData = orderDataSnapshot.getValue(itemListData::class.java)
itemListTable.add(newData)
}
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d(TAG, databaseError.getMessage()) //Don't ignore errors!
}
}
ordersRef.addValueEventListener(valueEventListener)
See, in order to get each order of each user, you should iterate twice, once to get the uidDataSnapshot and then to get the orderDataSnapshot object.

Trying to set a listener for Firebase chat messages

I am trying to set a listener for Firebase to get new chat messages and send them to a recycler view in a fragment, but I keep getting two errors:
The first error is on "Data" in setupAdapter and the second error is on message.timestamp.
Type Mismatch:
Required:
kotlin.collections.ArrayList<com.ntx_deisgns.cyberchatter.cyberchatter.Message> /* = java.util.ArrayList<com.ntx_deisgns.cyberchatter.cyberchatter.Message> */
Found:
kotlin.collections.ArrayList<android.os.Message> /* = java.util.ArrayList<android.os.Message> */
And
Unresolved reference: timestamp
Here's the code in my fragment class:
private fun createFirebaseListener(){
val postListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
val toReturn: ArrayList<Message> = ArrayList();
for(data in dataSnapshot.children){
val messageData = data.getValue<Message>(Message::class.java)
//unwrap
val message = messageData?.let { it } ?: continue
toReturn.add(message)
}
//sort so newest at bottom
toReturn.sortBy { message ->
message.timestamp
}
setupAdapter(toReturn)
}
override fun onCancelled(databaseError: DatabaseError) {
//log error
}
}
val database = FirebaseDatabase.getInstance()
val myRef = database.getReference("message")
val mDatabase: DatabaseReference? = myRef
mDatabase?.child("Group Chat")?.addValueEventListener(postListener)
}
/**
* Once data is here - display it
*/
private fun setupAdapter(data: ArrayList<Message>){
val linearLayoutManager = LinearLayoutManager(context)
viewManager = LinearLayoutManager(context)
viewAdapter = MessageAdapter(data) {
// Toast.makeText(this, "${it.text} clicked", Toast.LENGTH_SHORT).show()
}
//scroll to bottom
mainActivityRecyclerView.scrollToPosition(data.size - 1)
}
I have created a class called Message.kt and that is where it is trying to pull from. Here is that code as well just in case:
package com.ntx_deisgns.cyberchatter.cyberchatter
class Message {
constructor() //empty for firebase
constructor(messageText: String){
text = messageText
}
var text: String? = null
var timestamp: Long = System.currentTimeMillis()
}
I thought would be a fairly simple and straightforward task but it is turning out not a simple as I was thinking. What exactly am I missing here that is preventing me from doing this correctly?
I had import android.os.Message which was importing the wrong class. I needed to only have import com.ntx_deisgns.cyberchatter.cyberchatter.Message to import my Message class. Issue resolved.

Categories

Resources