firestore read data adding hashmap get data when added - android

when the user presses the button onlick ,read data and add hashmap
When completed I want to read with getfirestore2() method
data to come
comment1
comment2
comment3
comment4
but incoming data with getfirestore2() method
comment 4
my using method failed and not safe because I use delay
I how to fix codes?
my codes
binding.AllAskedBtn.setOnClickListener {
a1hliste.clear()
a2hliste.clear()
a3hliste.clear()
a4hliste.clear()
fun getfirestore2(){
println("listedekiler A1 "+a1hliste)
println("listedekiler A2 "+a2hliste)
println("listedekiler A3 "+a3hliste)
println("listedekiler A4 "+a4hliste)
println("\n veri 2 db size "+dbsize)
var sayi=0
var g1=a1hliste.getValue(sayi).toString()
var g2=a2hliste.getValue(sayi).toString()
var g3=a3hliste.getValue(sayi).toString()
var g4=a4hliste.getValue(sayi).toString()
for (a in 0..dbsize-1){
db.collection(g1).document(g2).collection(g3).document(g4).addSnapshotListener { task,e->
var veri= task!!.get("comment") as String
println("This is the data from "+veri)
}
}
}
db.collection("users").document(uuid).collection("Questions").orderBy("date",Query.Direction.DESCENDING).addSnapshotListener{ result, e->
var veri=result!!.documents
var verisize=result!!.documents.size
dbsize=0
for (a in veri){
var dersler=a.get("FBDersler") as String
var konular=a.get("FBKonular") as String
var sinavturu=a.get("sinavturu") as String
var QuestionUİD=a.get("QuestionUİD") as String
a1hliste.put(dbsize,sinavturu.toString())
a2hliste.put(dbsize,dersler.toString())
a3hliste.put(dbsize,konular.toString())
a4hliste.put(dbsize,QuestionUİD.toString())
dbsize+=1
GlobalScope.launch {
delay(2000L)
if (dbsize==verisize){
getfirestore2()
}
}
}
println("db size "+dbsize)
}
}

I finding solution
nested firestore listeners for , I am using .addOnSuccessListener method
binding.AllAskedBtn.setOnClickListener {
a1hliste.clear()
a2hliste.clear()
a3hliste.clear()
a4hliste.clear()
db.collection("users").document(uuid).collection("Questions").orderBy("date",Query.Direction.DESCENDING).get().addOnSuccessListener{ task->
var a=0
for (documents in task){
var dersler:String?=null
var konular:String?=null
var sinavturuu:String?=null
var QuestionUİD:String?=null
sinavturuu=documents["sinavturu"] as String
dersler=documents["FBDersler"] as String
konular=documents["FBKonular"] as String
QuestionUİD=documents["QuestionUİD"] as String
println(QuestionUİD.toString())
a1hliste.put(a,sinavturuu.toString())
a2hliste.put(a,dersler.toString())
a3hliste.put(a,konular.toString())
a4hliste.put(a,QuestionUİD.toString())
a+=1
}
}.addOnSuccessListener {
var Anumber=0
for (b in 0..a4hliste.size-1){
var g1=a1hliste.getValue(b).toString()
var g2=a2hliste.getValue(b).toString()
var g3=a3hliste.getValue(b).toString()
var g4=a4hliste.getValue(b).toString()
db.collection(g1).document(g2).collection(g3).document(g4).addSnapshotListener { value,e->
val Aphotourl=value!!.get("questionphotourl") as String
val Asinavtürü=value!!.get("sinavturu") as String
val Akonular=value!!.get("FBKonular") as String
val Adersler=value!!.get("FBDersler") as String
val Adocid=value!!.get("QuestionUİD") as String
val AFBusername=value!!.get("username") as String
val Aanswercheck=value!!.get("answercheck") as Boolean
Anumber+=1
usernamelist.add(AFBusername)
ProfileSınavTürü.put(Anumber,Asinavtürü)
ProfileFBDersler.put(Anumber,Adersler)
ProfileKonular.put(Anumber,Akonular)
ProfileQuestionUİD.put(Anumber,Adocid)
photoUrlList.put(Anumber,Aphotourl)
answercheckmap.put(Anumber,Aanswercheck)
adapter!!.notifyDataSetChanged()
}
}
}
}

Related

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.

How to get the Next and Previous piece of data from the Firestore Database using Kotlin in Android Studio?

I am trying to iterate through the database documents and have the fields update with the click of the next button and previous button. As of right now, it's only happening with one and then it stops. I don't know why it only does it once and stops. Any reason as to why? Here is a snapshot of what I got right now.
This image is hardcoded in there with the code that is below here.
[![private lateinit var locationTextView: TextView
private lateinit var descriptionTextView: TextView
private lateinit var previousArrow: Button
private lateinit var nextArrow: Button
private lateinit var usersImage: ImageView][2]][2]
val db = FirebaseFirestore.getInstance()
db.collection("Post").orderBy("timestamp")
val docRef = db.collection("Post").document("Puppy")
docRef.get()
.addOnSuccessListener { document ->
if (document != null) {
Log.d("Exists", "DocumentSnapshot data: ${document.data}")
// locationTextView.text = document.getString("locationTitle")
locationTextView.setText(document.getString("Name"))
descriptionTextView.setText(document.getString("description"))
val imageName = "Puppy"
val storageRef = FirebaseStorage.getInstance().reference.child("Images/$imageName")
val localFile = File.createTempFile("tempImage", "jpeg")
storageRef.getFile(localFile).addOnSuccessListener {
val bitmap = BitmapFactory.decodeFile(localFile.absolutePath)
usersImage.setImageBitmap(bitmap)
}
}
else {
Log.d("noExist", "No Such Document")
}
}
However, I want to be able to implement these two buttons, so I can get rid of the code above and have it just pull from the database and navigate through each Post with the click of the buttons. I added this onto my next button and it is able to iterate only once. Is there a way that I can iterate through the size of the documents, that way I can go through all of them and not just once?
nextArrow.setOnClickListener {
val first = db.collection("Post").orderBy("timestamp").limit(1)
first.get()
.addOnSuccessListener { documentSnapshots ->
val lastVisible = documentSnapshots.documents[documentSnapshots.size() - 1]
val next = db.collection("Post").orderBy("timestamp").startAfter(lastVisible).limit(2)
next.get().addOnSuccessListener { documents ->
for (document in documents) {
Log.d(TAG, "${document.id} => $${document.data}")
locationTextView.setText(document.getString("Name"))
descriptionTextView.setText(document.getString("description"))
}
}
val imageName = "Puppy"
val storageRef = FirebaseStorage.getInstance().reference.child("Images/$imageName")
val localFile = File.createTempFile("tempImage", "jpeg")
storageRef.getFile(localFile).addOnSuccessListener {
val bitmap = BitmapFactory.decodeFile(localFile.absolutePath)
usersImage.setImageBitmap(bitmap)
}
}
}
previousArrow.setOnClickListener {
}
Here is a snapshot of my database.
Again, to clarify, I am trying to iterate through my database with the Posts with the click of those buttons. Also, the images are stored in a firebase storage database since it's too large for firestore.
As also #FrankvanPuffelen mentioned in his comment, what you are looking for is a pagination algorithm. Since all your images are represented by a documents inside the "Post" collection, it means that you can create a query to load a single image and then load all subsequent images one at a time.
I have answered a similar question a few years ago, where the OP wanted to load more results on a button click:
Is there a way to paginate queries by combining query cursors using FirestoreRecyclerAdapter?
In your case, the first query would be:
val db = FirebaseFirestore.getInstance()
val firstQuery = db.collection("Post").orderBy("timestamp").limit(1)
To construct the second query, inside the first callback you have to get the last visible item using:
val lastVisible = queryDocumentSnapshots.documents[queryDocumentSnapshots.size() - indexFam]
And perform the query:
val next = db.collection("Post").orderBy("timestamp").startAfter(lastVisible).limit(1)
inside this function:
private fun getNext(first: Query, db: FirebaseFirestore, indexFam: Int) {
first.get().addOnSuccessListener { queryDocumentSnapshots ->
val lastVisible = queryDocumentSnapshots.documents[queryDocumentSnapshots.size() - indexFam]
val next = db.collection("Post").orderBy("timestamp").startAfter(lastVisible).limit(1)
next.get().addOnSuccessListener { documents ->
for (document in documents) {
Log.d(TAG, "${document.id} => $${document.data}")
// Whatever you want to iterate right here.
}
}
}
}
Here is how I solved this using my method onto my onCreate():
private lateinit var locationTextView: TextView
private lateinit var descriptionTextView: TextView
private lateinit var previousArrow: Button
private lateinit var nextArrow: Button
private lateinit var usersImage: ImageView
val db = FirebaseFirestore.getInstance()
val first = db.collection("Post").orderBy("timestamp")
getNext(first, db, 2) // This is the original pop up
getPostSizeAsync(first) { postSize ->
nextArrow.setOnClickListener {
nextClicker++
if(nextClicker == postSize) {
nextClicker = 2
}
getNext(first, db, nextClicker)
}
}
// This is for the previous button clicker
previousArrow.setOnClickListener {
if(nextClicker > 1)
{
nextClicker--
getNext(first, db, nextClicker)
}
}
And then with the private functions:
private fun getNext(first: Query, db: FirebaseFirestore, indexFam: Int) {
first.get().addOnSuccessListener { queryDocumentSnapshots ->
val lastVisible = queryDocumentSnapshots.documents[queryDocumentSnapshots.size() - indexFam]
val next = db.collection("Post").orderBy("timestamp").startAfter(lastVisible).limit(1)
next.get().addOnSuccessListener { documents ->
for (document in documents) {
Log.d(TAG, "${document.id} => $${document.data}")
locationTextView.setText(document.getString("Name"))
descriptionTextView.setText(document.getString("description"))
val imageName = document.getString("Name")
val storageRef = FirebaseStorage.getInstance().reference.child("Images/$imageName")
val localFile = File.createTempFile("tempImage", "jpeg")
storageRef.getFile(localFile).addOnSuccessListener {
val bitmap = BitmapFactory.decodeFile(localFile.absolutePath)
usersImage.setImageBitmap(bitmap)
}
}
}
}
}
private fun getPostSizeAsync(first: Query, callback: (Int) -> Unit) {
first.get().addOnSuccessListener { documents ->
val postSize = documents.count()
callback(postSize)
}
}

Accessing local variable in lambda in Kotlin

I want to save data acquired from Volley, But lambda used in VolleyRequest function(which gets json data from server) blocks it.
How should I change local variable that is in outside of lambda?
Thanks in advance.
class ConDataforReturn( val title:String , val imgDataList: ArrayList<ConImgData>)
fun getConData(context: Context, idx : String):ConDataforReturn{
val params = HashMap<String,String>()
var cd = arrayListOf<ConImgData>()
var title =""
params.put("package_idx",idx)
Log.e("idx size",idx.length.toString())
VolleyRequest(context,params,"https://dccon.dcinside.com/index/package_detail") { response ->
val answer = JSONObject(response)
var json = answer.getJSONArray("detail")
title = answer.getJSONObject("info").getString("title")
Log.d("title",title)//Prints right data
for (i in 0..(json.length() - 1)) {
val v = json.getJSONObject(i)
cd.add(ConImgData(v.getString("title"), v.getString("ext"), v.getString("path")))
}
}
return ConDataforReturn(title,cd)//returns ConDataforReturn("",arrayListOf<ConImgData>())
}
Here the the code from were you are calling this method
getConData(this, "id") { condata ->
}
Now, your method look like this,
fun getConData(context: Context, idx : String, returnConData : (condata : ConDataforReturn) -> Unit){
val params = HashMap<String,String>()
var cd = arrayListOf<ConImgData>()
var title =""
params.put("package_idx",idx)
Log.e("idx size",idx.length.toString())
VolleyRequest(context,params,"https://dccon.dcinside.com/index/package_detail") { response ->
val answer = JSONObject(response)
var json = answer.getJSONArray("detail")
title = answer.getJSONObject("info").getString("title")
Log.d("title",title)//Prints right data
for (i in 0..(json.length() - 1)) {
val v = json.getJSONObject(i)
cd.add(ConImgData(v.getString("title"), v.getString("ext"), v.getString("path")))
}
returnConData(ConDataforReturn(title,cd)) //returns ConDataforReturn("",arrayListOf<ConImgData>())
}
}

Android:can not save arrayList<String> in my firebase-realtime

I try to add accident in firebase-realtime with some information and some images, only the information are saved, the images paths are saved in firebase-storage but not in firebase-realtime.when I get the path of images from firebase-storage. I put this result path in arrayList of String. I want to put this arrayList in accident .I don't know why only the information are saved and the arrayList not saved, can some one help me please ?
class Accident (
var date:String?,
var id:String?,
var description:String?,
// var video:Array<String>?=null,
// var gravite:Int?=null,
val geolocalisation:Localisation?,
var photos: ArrayList<String>?,
val adress:String?
//var nbVictime:Int?=null
): Serializable
class Localisation( var altitude:Double=0.0,
var longitude:Double = 0.0)
: Serializable {
}
fun loadDatabase(firebaseData: DatabaseReference) {
uploadFile()
var loc = Localisation(currentLatLng!!.latitude, currentLatLng!!.longitude)
var uri=tabpath as ArrayList<String>
val availableAccident: List<Accident> = mutableListOf(
Accident(SimpleDateFormat("yyyy/MM/dd_HH:mm:ss").format(Date()), "", "il est tres grave", loc, uri, "Monastir")
)
var t=""
availableAccident.forEach {
val key = firebaseData.child("accidents").push().key
it.id = key!!
it.photos=uri
if (key != null) {
firebaseData.child("accidents").child(key).setValue(it)
}
}
}

Android (Kotlin) - How do I wait for an asynchronous task to finish?

I am new to Android and Kotlin and am currently working on a centralized API router class.
To achieve this I am using the Fuel Framework.
For the doAsync function, I use the Anko for Kotlin library.
To retrieve an authorization token from the API I currently use this method:
private fun Login(username: String, password: String, callback: (Map<Boolean, String>) -> Unit) {
"/auth/token.json".httpPost()
.header(mapOf("Content-Type" to "application/json"))
.body("""{"username":"$username", "password":"$password"}""", Charsets.UTF_8)
.response { request, response, result ->
request.headers.remove("Accept-Encoding")
when (result) {
is Result.Failure -> {
// val data = result.get()
val ex = result.getException()
val serverResponseJson = response.data.toString(Charsets.UTF_8)
var exceptionMessage = ex.message
val jelement = JsonParser().parse(serverResponseJson)
val jobject = jelement.asJsonObject
val serverResponseError = if (jobject.has("Error")) jobject.get("Error").asString else jobject.get("detail").asString
callback(mapOf(Pair(false, serverResponseError)))
}
is Result.Success -> {
val data = result.get()
val returnJson = data.toString(Charsets.UTF_8)
Log.println(Log.ASSERT, "RESULT_LOGIN", returnJson)
callback(mapOf(Pair(true, returnJson)))
}
}
}
}
I invoke this login method at
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
}[30, TimeUnit.SECONDS]
var test = Constants.id;
}
In a separate Constants class, I store the token and id like this:
class Constants {
companion object {
val baseUrl: String = "BASE_URL_TO_MY_API"
val contentTypeJson = "application/json"
lateinit var STOREAGE_PATH: String
// current user details
lateinit var id: String
lateinit var token: String
lateinit var refresh_token: String
// logged in User
lateinit var user: User
}
How do I make sure that the test variable is set after the asynchronous task is done? Currently, I run into
lateinit property id has not been initialized
I have come across the option to limit the task to a timeout such as I have done with [30, TimeUnit.SECONDS], unfortunately, this did not help.
Thanks for the help! Cheers.
I think the problem is where you want to access the result:
val btnLogin = findViewById<Button>(R.id.btn_login)
btnLogin.setOnClickListener { _ ->
doAsync {
val username = findViewById<EditText>(R.id.input_username_login)
val password = findViewById<EditText>(R.id.input_password_login)
var test: String? = null
Login(username.text.toString(), password.text.toString()) {
// Request was successful
if (it.containsKey(true)) {
// Parse return Json
// e.g. {"id":"36e8fac0-487a-11e8-ad4e-c471feb11e42","token":"d6897a230fd7739e601649bf5fd89ea4b93317f6","expiry":"2018-04-27T17:49:48.721278Z"}
val jelement = JsonParser().parse(it.getValue(true))
val jobject = jelement.asJsonObject
// save field for class-scope access
Constants.token = jobject.get("token").asString
Constants.id = jobject.get("id").asString
}
else{
Toast.makeText(this#LoginActivity, it.getValue(false), Toast.LENGTH_SHORT).show()
}
}
test = Constants.id // here test variable surely set if result was successful, otherwise it holds the null value
test?.let{
resultDelivered(it)
}
}[30, TimeUnit.SECONDS]
}
fun resultDelivered(id: String){
// here we know that the async job has successfully finished
}

Categories

Resources