I am trying to collect values from sqlite saved data and html value queried by jsoup inside a thread,the sqlite data was brought good,but the values inside thread that bring data from an html page didn't print anything.
why?
is this because i can't send a value outside a thread?,i made a global variable outside thread (stringbuilder) to catch data inside thread and send outside thread,but also this didn't work although i tested this thread in another page (outside database file) and works fine
what's wrong with this code?
DatabaseHandler.kt
fun allInvestments():List<InvestModelClass>{
val invList:ArrayList<InvestModelClass> = ArrayList<InvestModelClass>()
val selectQuery = "SELECT * FROM $TABLE_CONTACTS order by id desc"
val db = this.readableDatabase
var cursor: Cursor?
try{
cursor = db.rawQuery(selectQuery, null)
}catch (e: SQLiteException) {
db.execSQL(selectQuery)
return ArrayList()
}
// var invest_id: Int
var start_date: String
var amount:String
var currency: String
var karat:String
var enter_price:String
var percent:String
//moveToFirst will bring all ids
//moveToLast will bring the last inserted id
if (cursor.moveToFirst()) {
do {
// invest_id = cursor.getInt(cursor.getColumnIndex("id"))
start_date = cursor.getString(cursor.getColumnIndex("dt"))
amount = cursor.getString(cursor.getColumnIndex("amount"))
currency = cursor.getString(cursor.getColumnIndex("currency"))
karat = cursor.getString(cursor.getColumnIndex("karat"))
enter_price = cursor.getString(cursor.getColumnIndex("enter_price"))
if(enter_price.contains("إلى")){
var splitterx="إلى"
val split_strx=enter_price.split(splitterx)
enter_price=split_strx[0]
}else {
var splitterx=" "
val split_strx=enter_price.split(splitterx)
enter_price=split_strx[0]
}
//set percent,new amount
//calculate percent/profit and advice
if(currency=="الجنيه المصرى"){
url_link = "http://egypt.gold-price-today.com/"
}
if(currency=="الريال السعودي"){
url_link = "http://saudi-arabia.gold-price-today.com/"
}
if(currency=="الدرهم الإماراتى"){
url_link = "http://united-arab-emirates.gold-price-today.com/"
}
if(currency=="الدينار الكويتى"){
url_link = "http://kuwait.gold-price-today.com/"
}
//start thread to get current price
var catch_new_amount=StringBuilder()
var catch_percent=StringBuilder()
Thread(Runnable {
var init_recent_price=StringBuilder()
try{
val doc = Jsoup.connect(url_link).get()
val table = doc.select("table")[0]
val tbody = table.select("tbody")
val rows = tbody.select("tr")
for (i in rows.indices) {
val row = rows[i]//
val cols = row.select("td")
//karat 21
if (i == 2 ) {
karat_recent_price = cols[0].text()
if(karat_recent_price.contains("إلى")){
var splitter="إلى"
val split_str=karat_recent_price.split(splitter)
karat_recent_price=split_str[0]
init_recent_price.append(karat_recent_price) //show karat price text
}else {
var splitter=" "
val split_str=karat_recent_price.split(splitter)
karat_recent_price=split_str[0]
init_recent_price.append(karat_recent_price) //show karat price text
}
}
//karat 18
if (i == 3 ) {
karat_recent_price = cols[0].text()
if(karat_recent_price.contains("إلى")){
var splitter="إلى"
val split_str=karat_recent_price.split(splitter)
karat_recent_price=split_str[0]
init_recent_price.append(karat_recent_price) //show karat price text
}else {
var splitter=" "
val split_str=karat_recent_price.split(splitter)
karat_recent_price=split_str[0]
init_recent_price.append(karat_recent_price) //show karat price text
}
}
//karat 24
if (i == 0 ) {
karat_recent_price = cols[0].text()
if(karat_recent_price.contains("إلى")){
var splitter="إلى"
val split_str=karat_recent_price.split(splitter)
karat_recent_price=split_str[0]
init_recent_price.append(karat_recent_price) //show karat price text
}else {
var splitter=" "
val split_str=karat_recent_price.split(splitter)
karat_recent_price=split_str[0]
init_recent_price.append(karat_recent_price) //show karat price text
}
}
//inv_amount[index] , inv_enter_price[index] , karat_recent_price
var int_amount=amount
// new_amount=(int_amount * int_recent_price_string / int_enter_price).toString()
new_amount= karat_recent_price
catch_new_amount.append(new_amount)
if(new_amount > int_amount){
var inc=new_amount.toFloat() - int_amount.toFloat()
var percent_inc=(inc/int_amount.toFloat())* 100
percent=percent_inc.toString()
init_percent_string=percent
catch_percent.append(percent)
}else {
var dec=int_amount.toFloat() - new_amount.toFloat()
var percent_dec=(dec/int_amount.toFloat()) * 100
percent=percent_dec.toString()
init_percent_string=percent
catch_percent.append(percent)
}
}//end for
}
catch(e: IOException){
e.printStackTrace()
}
// runOnUiThread{}
}).start()
//id,start_date,enter_price,amount,currency,karat,percent,profit
val emp= InvestModelClass(
start_date = start_date,
enter_price = enter_price,
amount = amount,
currency = currency,
karat = karat,
new_amount = catch_new_amount.toString(),
percent = catch_percent.toString()
)
invList.add(emp)
} while (cursor.moveToNext())
}
return invList
}
ListInvests.kt
package com.example.parsehtml
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.os.Bundle
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.android.synthetic.main.list_invests.*
import kotlinx.android.synthetic.main.startinvest_main.*
class ListInvests : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.startinvest_main)
button.setOnClickListener(){
intent = Intent(this,MainActivity::class.java)
startActivity(intent)
}
//creating the instance of DatabaseHandler class
val databaseHandler: DatabaseHandler= DatabaseHandler(this)
//calling the viewEmployee method of DatabaseHandler class to read the records
//id,start_date,enter_price,amount,currency,karat,percent,profit
val inv: List<InvestModelClass> = databaseHandler.allInvestments()
val inv_startdate = Array<String>(inv.size){"0"}
val inv_enter_price = Array<String>(inv.size){"null"}
val inv_amount = Array<String>(inv.size){"null"}
val inv_currency = Array<String>(inv.size){"null"}
val inv_karat = Array<String>(inv.size){"null"}
val inv_new_amount=Array(inv.size){"null"}
val inv_percent=Array(inv.size){"null"}
var index = 0
for(e in inv){
inv_startdate[index] = e.start_date.toString()
inv_enter_price[index] = e.enter_price.toString()
inv_amount[index] = e.amount.toString()
inv_currency[index] = e.currency.toString()
inv_karat[index] = e.karat.toString()
inv_new_amount[index]=e.new_amount.toString()
inv_percent[index]=e.percent.toString()
index++
}
//creating custom ArrayAdapter
//id,start_date,enter_price,amount,currency,karat,percent,profit
val myListAdapter = InvestListAdapter(this,inv_startdate,inv_enter_price,inv_amount,inv_currency,inv_karat,inv_new_amount,inv_percent)
listView.adapter = myListAdapter
}//end oncreate
}
InvestModelClass.kt
package com.example.parsehtml
//id,start_date,enter_price,amount,currency,karat,percent,profit
class InvestModelClass ( val start_date:String ,val enter_price:String, val amount: String,val currency:String,val karat:String,val new_amount:String,val percent:String)
Related
val energy = enerjiText.text.toString()
if (energy < 50 .toString()){
progressBar.max = 1000
val currentProgress = 900
ObjectAnimator.ofInt(progressBar,"progress",currentProgress)
.setDuration(2000)
.start()
progressBar2.max = 1000
val currentProgress2 = 700
ObjectAnimator.ofInt(progressBar2,"progress",currentProgress2)
.setDuration(2000)
.start()
}
else{
progressBar.max = 1000
val currentProgress = 600
ObjectAnimator.ofInt(progressBar,"progress",currentProgress)
.setDuration(2000)
.start()
progressBar2.max = 1000
val currentProgress2 = 300
ObjectAnimator.ofInt(progressBar2,"progress",currentProgress2)
.setDuration(2000)
.start()
}
The id of the textView circled in black is enerjiText. In this textView I am printing the data that I have saved in firestore. But without checking the values in the if-else loop you see in the code, the progress bars inside the if push the screen. I gave the value of energy < 50 in the if, the answer is 227, but it shows the progress bar values in the if without going into the else loop. If I don't convert the enerjiText and the energy < 50 control to string I get the error "java lang numberformatexception For input string"
val panelC = binding.panelCount3.text.toString()
val landSl = binding.landSlope.text.toString()
val landS = binding.landSize3.text.toString()
val billT = binding.bill.text.toString()
if (panelC.isEmpty() || landSl.isEmpty() || landS.isEmpty() || billT.isEmpty()){
Toast.makeText(requireContext(),"Alanları Doldurun",Toast.LENGTH_LONG).show()
}
else{
val pco = panelC.toInt()
val lsl = landSl.toInt()
val ls = landS.toInt()
val b = billT.toInt()
val enerji = Math.round((((ls * pco) + (lsl * b)) / 100).toDouble())
val postMap = hashMapOf<String,Any>()
postMap.put("Panel Sayisi",binding.panelCount3.text.toString())
postMap.put("Arazi Eğimi",binding.landSlope.text.toString())
postMap.put("Arazi Boyutu",binding.panelCount3.text.toString())
postMap.put("Fatura",binding.bill.text.toString())
postMap.put("Enerji",enerji.toDouble())
postMap.put("date",com.google.firebase.Timestamp.now())
firestore.collection("Enerji").add(postMap).addOnSuccessListener {
}.addOnFailureListener {
Toast.makeText(requireContext(),it.localizedMessage,Toast.LENGTH_LONG).show()
}
These codes are the codes where the calculations of the number that should be written in enerjiText are made and saved in firestore.
private fun getData(){
db.collection("Enerji").addSnapshotListener { value, error ->
if (error!=null){
Toast.makeText(requireContext(),error.localizedMessage,Toast.LENGTH_LONG).show()
}else{
if (value !=null){
if (!value.isEmpty){
val documents = value.documents
for (document in documents){
val panelSayisi = document.get("Panel Sayisi") as String
val araziEgimi = document.get("Arazi Eğimi") as String
val araziBoyutu = document.get("Arazi Boyutu") as String
val faturaDegeri = document.get("Fatura") as String
val enerji = Enerji(panelSayisi, araziEgimi, araziBoyutu, faturaDegeri)
postArrayList.add(enerji)
}
}
}
}
val db = FirebaseFirestore.getInstance()
db.collection("Enerji").orderBy("date", Query.Direction.DESCENDING).limit(1)
.get()
.addOnCompleteListener {
val result : StringBuffer = StringBuffer()
if (it.isSuccessful){
for (document in it.result){
result.append(document.data.getValue("Enerji"))
}
enerjiText.setText(result)
}
}
}
These are the codes that I get the data I saved in firebase above and wrote into EnerjiText.
data class Enerji (val panelSayisi : String, val araziEgimi : String, val araziBoyutu : String, val faturaDegeri : String)
Finally, the Enerji class defined inside the postArrayList, which I think might be the source of the error.
I have an activity with two spinners. I have made arrays for each spinner containing data from popular foods, but I want the user to be able to add three of their own selections to the lists. The app compiles and installs and runs, BUT when I select the specific activity, the screen closes and either goes to the apps main screen or to the emulator's home screen. Logcat shows:-
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.kotlinsql/com.example.kotlinsql.CarbsInput}: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.SharedPreferences android.content.Context.getSharedPreferences(java.lang.String, int)' on a null object reference
It's where I call the shared preferences.
I have tried different contexts but still get errors that vary slightly according to the context and I have included them in the code as remarks.
I have tried moving everything into onCreate, but this gives me an error in the class definition line, because the function "override fun onItemSelected" seems to have to be stand-alone, so must be outside onCreate.
Please help. I have only been learning this for less than a year, and I apologise for any stupid mistakes. No offence is intended.
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.*
import kotlinx.android.synthetic.main.input_carbs.*
import java.time.Clock
import java.time.LocalDateTime
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter
import android.content.SharedPreferences
import android.content.res.Configuration
import java.security.AccessController.getContext
import kotlin.math.*
class CarbsInput : AppCompatActivity(),AdapterView.OnItemSelectedListener {
var spinner:Spinner? = null
var spinner2:Spinner? = null
val sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences by lazy { getSharedPreferences(sharedPrefFile, MODE_PRIVATE) }
val dataModel: CarbsInputModel by lazy { CarbsInputModel(sharedPreferences) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.input_carbs)
spinner = this.gi_spinner
spinner2 = this.carbs_per_spinner
// Create an ArrayAdapter using a simple spinner layout and gIndices array
val aa = ArrayAdapter(this, android.R.layout.simple_spinner_item, dataModel.gIndices)
// Set layout to use when the list of choices appear
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Set Adapter to Spinner
spinner!!.setAdapter(aa)
//spinner!!.setSelection(9)//optional, better to leave favourites at top
val aa2 = ArrayAdapter(this, android.R.layout.simple_spinner_item, dataModel.carbsPer)
aa2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinner2!!.setAdapter(aa2)
input_carbs_btn.setOnClickListener {//set an onclick listener
enterCarbs() }
backbtn.setOnClickListener {
val fourth = Intent(this, MainActivity::class.java)//sets "fourth" to be MainActivity
// start your next activity
startActivity(fourth) }
btn_view_carbs.setOnClickListener { viewCarbs() }
btn_carb_calc.setOnClickListener {
var carbPer = et_carbsper.text.toString().toLong()
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) /100.0).toLong()
et_carbs.setText(carbs.toString())
}//end of button onClick listener
}//end of on create
fun enterCarbs(){//get inputs from keys and calculate carbLife using GI
var noow = ZonedDateTime.now(Clock.systemUTC())
var noowSecs: Long = noow.toEpochSecond()
var noowMins: Long = (noowSecs) / 60
//var carbLife:Long = 220// this has to be calculated from GI
var nowLocal = LocalDateTime.now()
var carbTime: Long = noowMins-1
var showCarbTime: String = nowLocal.format(DateTimeFormatter.ofPattern("E d MMM kk:mm "))+"local"
var sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences = getSharedPreferences(sharedPrefFile, Context.MODE_PRIVATE)
val databaseHandler: DatabaseHandler = DatabaseHandler(this)
if (et_carbs.text.toString().trim() != "" && et_carbGI.text.toString().trim() != "") {
val carbs = et_carbs.text.toString().toLong()
val carbGi = (et_carbGI.text.toString().toLong())
//val carbLife = 12_000 /carbGi.toLong()// to be replaced with 1-(X/L)^n calculation in stage2
var carbDecayIndex:Double= sharedPreferences.getFloat("carbDecayIndex_key",0.8F).toDouble()//n
//public fun carbLifeCalc():Double//L = 10^((log120^n-logGI)/n)
var logLtoN = log10(120.00.pow(carbDecayIndex))//log120^n
var logGi = log10(carbGi / 100.00)//logGI
var carbLife = 10.00.pow((logLtoN - logGi) / carbDecayIndex).toLong()//gives L
//end of carbLifeCalculation
val status =
databaseHandler.saveCarbs(CarbsModelClass(carbTime, showCarbTime, carbs, carbGi, carbLife))
if (status > -1) {
Toast.makeText(applicationContext, "Carbohydrate saved", Toast.LENGTH_LONG).show()
//MainActivity.evaluateCarbs //want to call this function from here without writing it again
et_carbs.text.clear()
et_carbGI.text.clear()
}
} else {
Toast.makeText(
applicationContext,
"No field can be blank enter GI as 50 if unknown",
Toast.LENGTH_LONG
).show()
}
}//end of function entercarbs
fun viewCarbs() {
//creating the instance of DatabaseHandler class
val databaseHandler: DatabaseHandler = DatabaseHandler(this)
//calling the viewCarbs method of DatabaseHandler class to read the records
val carbohs: List<CarbsModelClass> = databaseHandler.viewCarbs()
//val carbohsArraycarbTime = Array<String>(carbohs.size) { "null" }//not needed
val carbohsArrayshowCarbTime = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbs = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbGI = Array<String>(carbohs.size) { "null" }
val carbohsArraycarbLife = Array<String>(carbohs.size) { "null" }
var index = 0
for (e in carbohs) {
//carbohsArraycarbTime[index] = e.carbTime.toString()//not needed
carbohsArrayshowCarbTime[index] = e.showCarbTime
carbohsArraycarbs[index] = e.carbs.toString()
carbohsArraycarbGI[index] = e.carbGi.toString()//note small i inGi
carbohsArraycarbLife[index] = e.carbLife.toString()
//index--
index++
}
//creating custom ArrayAdapter
val myCarbListAdapter = CarbListAdapter(
context = this,
//carbTime = carbohsArraycarbTime,//not needed
showCarbTime = carbohsArrayshowCarbTime,
carbs = carbohsArraycarbs,
carbGI = carbohsArraycarbGI,
carbLife = carbohsArraycarbLife
)
lv_carb_view.adapter = myCarbListAdapter
}//end of fun view carbs
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
// see https://stackoverflow.com/questions/9262871/android-two-spinner-onitemselected
if(parent?.getId() == R.id.gi_spinner) {
var giFullSelected = dataModel.gIndices[position]
var gIprelimString : String =
giFullSelected[0].toString() + giFullSelected[1]//selecting just digits
var GIprelim = gIprelimString.toLong()
et_carbGI.setText(gIprelimString)
}//end of first if
else{ if (parent?.getId() == R.id.carbs_per_spinner) {
var carbPerFullSelected = dataModel.carbsPer[position]
var carbPerString: String =
carbPerFullSelected[0].toString() + carbPerFullSelected[1]
var carbPer = carbPerString.toLong()
et_carbsper.setText(carbPerString)
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) /100.0).toLong()
et_carbs.setText(carbs.toString())}//end of second if
else { Toast.makeText(applicationContext, "parent id "+parent?.getId().toString(), Toast.LENGTH_LONG).show()
}//end of second else
}//end of elseif OR /first else
}//end of on item selected
override fun onNothingSelected(parent: AdapterView<*>?) { }
}//end of class carbs input
New Class CarbsInputModel Below
//start of CarbsInputModel
import android.content.SharedPreferences
class CarbsInputModel(private val sharedPreferences:SharedPreferences) {
// val sharedPrefFile = "greenbandbasicpreference"
//val sharedPreferences:SharedPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
val sharedFav1Value: String? = sharedPreferences.getString("fav1_key", "50 50 defaultone")
val sharedFav2Value: String? = sharedPreferences.getString("fav2_key", "50 50 defaultwo")
val sharedFav3Value: String? = sharedPreferences.getString("fav3_key", "50 50 defaulthree")
val favDescr1:String = sharedFav1Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favDescr2:String = sharedFav2Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favDescr3:String = sharedFav3Value?.takeLastWhile { !it.isDigit() }.toString().trim()
val favData1:String = sharedFav1Value?.takeWhile { !it.isLetter() } .toString()
val favData2:String = sharedFav2Value?.takeWhile { !it.isLetter() } .toString()
val favData3:String = sharedFav3Value?.takeWhile { !it.isLetter() } .toString()
val favCarbPerString1 = favData1.take(3).trim()
val favCarbPerString2 = favData2.take(3).trim()
val favCarbPerString3 = favData3.take(3).trim()
val favGiString1 = favData1.takeLast(4).trim()
val favGiString2 = favData2.takeLast(4).trim()
val favGiString3 = favData3.takeLast(4).trim()
val favFullCarbPer1 = favCarbPerString1+" "+favDescr1+" "
val favFullCarbPer2 = favCarbPerString2+" "+favDescr2+" "
val favFullCarbPer3 = favCarbPerString3+" "+favDescr3+" "
val favFullGi1 = favGiString1+" "+favDescr1+" "
val favFullGi2 = favGiString2+" "+favDescr2+" "
val favFullGi3 = favGiString3+" "+favDescr3+" "
}//end of class Carbs Input Model
Attempted tidy code for override function. This still does absolutely Nothing
//trying to tidy up code
override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) {
when {
parent.id == R.id.gi_spinner -> {
var giFullSelected = dataModel.gIndices[position]
var gIprelimString: String =
giFullSelected[0].toString() + giFullSelected[1]//selecting just leading digits
et_carbGI.setText(gIprelimString)
}
parent.id == R.id.carbs_per_spinner -> {
var carbPerFullSelected = dataModel.carbsPer[position]
var carbPerString: String =
carbPerFullSelected[0].toString() + carbPerFullSelected[1]
var carbPer = carbPerString.toLong()
et_carbsper.setText(carbPerString)
var weight = et_weight.text.toString().toLong()
var carbs = round((weight * carbPer) / 100.0).toLong()
et_carbs.setText(carbs.toString())
}
else -> {
Toast.makeText(applicationContext, "parent id " + parent?.getId().toString(),
Toast.LENGTH_LONG ).show()
}
}//end of when
}//end of override fun onitemselected
When you assign a value at the declaration site like this:
val sharedPreferences:SharedPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
the function(s) you are calling to create the object that will be assigned to the property is getting called at the time the Activity is instantiated by Android. Unfortunately, this is too early to be calling anything that relies on the Activity being fully instantiated and set up, for example, anything that needs the Context as a constructor parameter.
The easy fix for this is to make these properties instantiate themselves lazily, so they are created after the Activity is already fully instantiated:
val sharedPreferences: SharedPreferences by lazy { getSharedPreferences(sharedPrefFile, MODE_PRIVATE) }
An alternate solution is the use a lateinit var and prepare the item in onCreate():
lateinit var sharedPreferences: SharedPreferences
// ...
override fun onCreate(bundle: SavedInstanceState) {
super.onCreate(bundle)
sharedPreferences = getSharedPreferences(sharedPrefFile, MODE_PRIVATE)
}
I usually prefer the lazy method because it avoid splitting the declaration and the assignment so the code is easier to read. And it allows you to use val instead of var so the intent is clearer.
However, you also have many properties that are reliant on the SharedPreference instance, so they would all have to use one of the above solutions as well, which will lead to very verbose code. I recommend that you move all of these properties into a separate class that uses the SharedPreferences as a constructor paraamter. For example:
class CarbsInputModel(private val sharedPreferences: SharedPreferences) {
val sharedFav1Value: String? = sharedPreferences.getString("fav1_key", "50 50 defaultone")
val sharedFav2Value: String? = sharedPreferences.getString("fav2_key", "50 50 defaultwo")
val sharedFav3Value: String? = sharedPreferences.getString("fav3_key", "50 50 defaulthree")
// etc...
}
and then in your activity:
class CarbsInput : AppCompatActivity(),AdapterView.OnItemSelectedListener {
var spinner:Spinner? = null
var spinner2:Spinner? = null
val sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences: SharedPreferences by lazy { getSharedPreferences(sharedPrefFile, MODE_PRIVATE) }
val dataModel: CarbsInputModel by lazy { CarbsInputModel(sharedPreferences) }
}
And then access your properties through the dataModel property. It is also better design practice to separate your UI and your functions that modify the data, so you could put those functions in your data model class.
You might also want to read up on how to use a ViewModel class. It would possibly be a more scalable solution than what I put above.
I am not able to retrieve data.
Is there any way by which we can access our database and we can check what we have inserted so far.
In this code I am trying to insert the latest calculation I did in my calculator with the number of my transaction. Using Coroutines, Room and View Model.
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.navigation.NavController
import com.kotlin_developer.calculator.database.CalculationDatabaseDao
import com.kotlin_developer.calculator.database.CalculatorHistory
import kotlinx.coroutines.*
import timber.log.Timber
class CalculatorViewModel(
val
database: CalculationDatabaseDao,
application: Application
) : AndroidViewModel(application) {
var operatorEnabled: Boolean = false
var firstResult: Double = 0.0
var operator: Char = ' '
var ifNumeric: Boolean = true
var secondResultLenght = 0
// First step of coroutines is to create job, this can cancel all the coroutines started by this view model
private var viewModelJob = Job()
// Second step is to create the scope where we want to run our code
// Scope determines what thread the coroutines will run on, it also needs to know about the job
private val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
// private val history = database.getAllCalculation()
private var _totalTransaction = MutableLiveData<Int>()
val totalTransaction: LiveData<Int>
get() = _totalTransaction
//Getting currentCalculation
private var _currentCalculation = MutableLiveData<String>()
val currentCalculation: LiveData<String>
get() = _currentCalculation
//Getting current result
private var _currentResult = MutableLiveData<String>()
val currentResult: LiveData<String>
get() = _currentResult
val navControler = MutableLiveData<NavController>()
private val _secondResult = MutableLiveData<Double>()
val secondResult: LiveData<Double>
get() = _secondResult
private var _resultTextValue = MutableLiveData<Double>()
val resultTextValue: LiveData<Double>
get() = _resultTextValue
private var _lastHistory = MutableLiveData<CalculatorHistory>()
val lastHistory: LiveData<CalculatorHistory>
get() = _lastHistory
val totalCalculation = mutableListOf<String>()
init {
Timber.i("Calculator View Model created")
_resultTextValue.value = 0.0
_secondResult.value = 0.0
_totalTransaction.value = 0
}
fun insertData() {
uiScope.launch {
val newHistory = CalculatorHistory(
totalTransaction.value?.toLong()!!,
totalCalculation[totalTransaction.value!! - 1]
)
insert(newHistory)
}
}
private suspend fun insert(newHistory: CalculatorHistory) {
withContext(Dispatchers.IO) {
database.insert(newHistory)
Timber.i("Data Inserted")
}
}
internal fun initializeHistory() {
uiScope.launch {
_lastHistory.value = getHistoryFromDatabase()
}
Timber.i("${lastHistory.value?.transactionNumber} and ${lastHistory.value?.calculation}")
}
private suspend fun getHistoryFromDatabase(): CalculatorHistory? {
return withContext(Dispatchers.IO) {
var lastCalculation = database.get(1)
lastCalculation
}
Timber.i("${lastHistory.value?.transactionNumber} and ${lastHistory.value?.calculation}")
}
fun calculator(operator: Char): Double {
return when (operator) {
'+' -> firstResult.plus(secondResult.value ?: 0.0)
'-' -> firstResult.minus(secondResult.value ?: 0.0)
'*' -> firstResult.times(secondResult.value ?: 1.0)
'/' -> firstResult.div(secondResult.value ?: 1.0)
else -> firstResult.rem(secondResult.value ?: 1.0)
}
}
fun createCalculation() {
ifNumeric = false
operatorEnabled = false
_resultTextValue.value = calculator(operator)
//This we can use in future to create a list of calculation
totalCalculation.add(
totalTransaction.value!!,
"${currentCalculation.value}=${_resultTextValue.value}"
)
_currentResult.value = totalCalculation[totalTransaction.value!!]
insertData()
firstResult = _resultTextValue.value ?: 0.0
_totalTransaction.value = _totalTransaction.value?.plus(1)
_secondResult.value = 0.0
secondResultLenght = 0
}
fun seprateNumber(number: Double) {
if (operatorEnabled) {
if (ifNumeric) {
_secondResult.value = number + (secondResult.value?.times(10.0))!!
} else {
_secondResult.value = number
}
} else {
firstResult = number + (firstResult * 10)
}
}
fun clearText() {
_resultTextValue.value = 0.0
_currentResult.value = ""
firstResult = 0.0
_secondResult.value = 0.0
secondResultLenght = 0
operator = ' '
operatorEnabled = false
ifNumeric = false
_currentCalculation.value = ""
}
fun ifSeprateNumber(number: Double) {
seprateNumber(number)
if (operatorEnabled) {
secondCalculationText()
} else {
_currentCalculation.value = firstResult.toString()
}
ifNumeric = true
}
fun secondCalculationText() {
_currentCalculation.value = _currentCalculation.value
?.removeRange(
_currentCalculation.value!!.length -
secondResultLenght, _currentCalculation.value!!.length
)
_currentCalculation.value =
"${_currentCalculation.value}${secondResult.value.toString()}"
secondResultLenght = secondResult.value.toString().length
ifNumeric = true
}
fun addTextToField() {
ifNumeric = false
operatorEnabled = true
_currentCalculation.value = "${_currentCalculation.value}${operator}"
}
override fun onCleared() {
super.onCleared()
//This cancels all the coroutines when the view model is getting closed
viewModelJob.cancel()
Timber.i("Calculator ViewModel destroyed")
}
}
I think you are doing too many things in your ViewModel. If you want to see what the data in your database is, you should have an activity (the View, the activity which creates, updates and handles UI events) that observes your livedata in the viewmodel. Everytime you insert values into your database, regardless of the time it takes, once it's done it will trigger a callback to the observer and you will get the new values. I would start with this, so you can keep track of what you are inserting in your database, and it's the starting point to then using those values.
Would be something like this, from your View/Activity:
yourViewModel.variableYouWant.observe(this, Observer { yourValue ->
//do something with yourValue
})
I want to access and change the text of a TextView from a non parent file.
This is the class I would like to make the edits from
class BarcodeProcessor(graphicOverlay: GraphicOverlay, private val workflowModel: WorkflowModel) :
FrameProcessorBase<List<FirebaseVisionBarcode>>() {
In this class there is no onCreat()
Do I need to have AppCompatActivity() in order to gain access to the xml layout files?
Inside this file is where the barcode is processed and I can display the results along with a SQL query to the console, but I want to update the interface to reflect those values. Can I pass them through the companion oject to the parent class?
Is there any way I can make the layout updates from the BarcodeProcessor file?
I've tried including imports
import kotlinx.android.synthetic.main.barcode_field.*
#MainThread
override fun onSuccess(
image: FirebaseVisionImage,
results: List<FirebaseVisionBarcode>,
graphicOverlay: GraphicOverlay
) {
var z = ""
var isSuccess: Boolean? = false
//Variables used in the SQL query
var strData = ""
var strData2 = ""
try {
con = dbConn() // Connect to database
if (con == null) {
z = "Check Your Internet Access!"
} else {
val query = "select [ValueOne], [ValueTwo] from [TableName] where [ValueOne] = '$barcodeValue'"
val stmt = con!!.createStatement()
val cursor = stmt.executeQuery(query)
barcodeValue = results[0].getRawValue()!!
println(barcodeValue)
if (cursor.next()) {
//Selects the values from the columns in the table where the query is taking place
strData = cursor.getString("[ValueOne]")
strData2 = cursor.getString("[ValueTwo]")
var finalDispositionID = strData
var moduleSizeID = strData2
println(finalDispositionID)
println(moduleSizeID)
//barcode_field_value3 is the name of the textview
//barcode_field_value2 is the name of the other textview
//barcode_field_value3.text = strData
z = "Login successful"
isSuccess = true
con!!.close()
} else {
z = "Invalid Credentials!"
isSuccess = false
}
}
} catch (ex: java.lang.Exception) {
isSuccess = false
z = ex.message!!
}
The query happens in the function onSuccess(). I included the entire file for reference though.
LiveBarcodeScanningActivity
private fun setUpWorkflowModel() {
workflowModel = ViewModelProviders.of(this).get(WorkflowModel::class.java)
// Observes the workflow state changes, if happens, update the overlay view indicators and
// camera preview state.
workflowModel!!.workflowState.observe(this, Observer { workflowState ->
if (workflowState == null || Objects.equal(currentWorkflowState, workflowState)) {
return#Observer
}
currentWorkflowState = workflowState
Log.d(TAG, "Current workflow state: ${currentWorkflowState!!.name}")
val wasPromptChipGone = promptChip?.visibility == View.GONE
when (workflowState) {
WorkflowState.DETECTING -> {
promptChip?.visibility = View.VISIBLE
promptChip?.setText(R.string.prompt_point_at_a_barcode)
startCameraPreview()
}
WorkflowState.CONFIRMING -> {
promptChip?.visibility = View.VISIBLE
promptChip?.setText(R.string.prompt_move_camera_closer)
startCameraPreview()
}
WorkflowState.SEARCHING -> {
promptChip?.visibility = View.VISIBLE
promptChip?.setText(R.string.prompt_searching)
stopCameraPreview()
}
WorkflowState.DETECTED, WorkflowState.SEARCHED -> {
promptChip?.visibility = View.GONE
stopCameraPreview()
}
else -> promptChip?.visibility = View.GONE
}
val shouldPlayPromptChipEnteringAnimation = wasPromptChipGone && promptChip?.visibility == View.VISIBLE
promptChipAnimator?.let {
if (shouldPlayPromptChipEnteringAnimation && !it.isRunning) it.start()
}
})
workflowModel?.detectedBarcode?.observe(this, Observer { barcode ->
if (barcode != null) {
val barcodeFieldList = ArrayList<BarcodeField>()
barcodeFieldList.add(BarcodeField("Module Serial Number", barcode.rawValue ?: ""))
BarcodeResultFragment.show(supportFragmentManager, barcodeFieldList)
}
})
}```
You are setting workflowModel.detectedBarcode live data in BarcodeProcessor. So you can find where you are passing this workflowModel to BarcodeProcessor constructor and add such a code to observe this live data in your activtiy:
workflowModel.detectedBarcode.observe(this, Observer {
myTextView.text = it
})
This answer pertains directly to displaying results in the Google ML-Kit Showcase sample project. So if your working on that with barcode scanning this is related.
In order to display the results on successful scan you need to add the fields into the
BarcodeFieldAdapter file
internal class BarcodeFieldViewHolder private constructor(view: View) : RecyclerView.ViewHolder(view) {
private val labelView: TextView = view.findViewById(R.id.barcode_field_label)
private val valueView: TextView = view.findViewById(R.id.barcode_field_value)
private val moduleSize: TextView = view.findViewById(R.id.textViewModuleSize)
private val finalDisposition: TextView = view.findViewById(R.id.textViewFinalDisp)
private val NCRNotes: TextView = view.findViewById(R.id.textViewNCR)
private val OverlapLengthText: TextView = view.findViewById((R.id.textViewOverlapLength))
fun bindBarcodeField(barcodeField: BarcodeField) {
//These are the original values that display in the Barcode_field.xml
labelView.text = barcodeField.label
valueView.text = barcodeField.value
//These are the new values I passed in
moduleSize.text = BarcodeProcessor.data.moduleSize
finalDisposition.text = BarcodeProcessor.data.finalDisposition
NCRNotes.text = BarcodeProcessor.data.NCRNotes
OverlapLengthText.text = BarcodeProcessor.data.OverlapLength
}
Inside
BarcodeProcessor
declare a data object
object data {
var finalDisposition = "init"
var moduleSize = "init"
var barcodeValue: String = ""
var NCRNotes = ""
var OverlapLength = ""
}
These variables will be passed into the BarcodeFieldAdapter file above then displayed in the barcode_field.xml on success.
Inside the query function - That is in the BarcodeProcessor file
//Selects the values from the columns in the table where the query is taking place
strData = cursor.getString("Column Name 1")
strData2 = cursor.getString("Column Name 2")
strData4 = cursor.getString("Column Name 3")
//Assigns the values to the variables inside the object data
data.finalDisposition = strData
data.moduleSize = strData2
data.OverlapLength = strData4
data.NCRNotes = ""
Finally, inside the barcode_field.xml file, create TextViews with the (R.id.TextViewNAMES) that are shown above in the BarcodeFieldAdapter file. Now on successful scanning the new values from the query will be displayed.
I have some problem when I try to save array data from an Android Kotlin to Firebase Database.
I want to save data as shown below:
Image 1
but, when I saved the data, in the Firebase database it always updated the "pemasukan" object, not creating new data.
the stored data is always like the picture below
Image 2
Anyone can help me please.
Sorry for my English ;)
This is the code I am using for
class PemasukanActivity : AppCompatActivity() {
private lateinit var database : FirebaseDatabase
private lateinit var mRef : DatabaseReference
lateinit var currentDate : String
private val id_user = "agus"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pemasukan)
btnSimpan()
}
//aksi tombol untuk simpan data
private fun btnSimpan(){
btn_simpan_pemasukan.setOnClickListener {
val pemasukan = Pemasukan()
if (edt_nama_barang.length() > 3){
pemasukan.keterangan = edt_nama_barang.text.toString()
} else {
edt_nama_barang.error ="Nama Barang Harus lebih dari 3 huruf"
return#setOnClickListener
}
if (edt_jumlah.length() > 3){
var jumlah = edt_jumlah.text.toString()
pemasukan.jumlah = jumlah.toLong()
}else{
edt_jumlah.error ="Jumlah Harus lebih dari 3 huruf"
return#setOnClickListener
}
simpanData(pemasukan)
}
}
//simpan data ke firebase
private fun simpanData(listPemasukan :Pemasukan){
val sdf = SimpleDateFormat("ddMyyyy")
currentDate = sdf.format(Date())
val data = Dataitem()
data.id = currentDate
data.user_id = id_user
val nameList = ArrayList<Pemasukan>(Arrays.asList(listPemasukan))
data.pemasukan = nameList
database = FirebaseDatabase.getInstance()
mRef = database.getReference("pendataan")
mRef.child("${currentDate}").setValue(data)
.addOnSuccessListener {
Toast.makeText(this, "Success . .", Toast.LENGTH_LONG).show()
edt_nama_barang.text.clear()
edt_jumlah.text.clear()
}
.addOnFailureListener {
Toast.makeText(this, "Gagal Input Data", Toast.LENGTH_LONG).show()
it.printStackTrace()
Log.e("FIREBASE_ERROR", it.message)
}
}
And the POJO class like this:
data class Dataitem (
var id : String = "",
var user_id : String = "",
var pemasukan : ArrayList<Pemasukan>? = null,
var pengeluaran : ArrayList<Pengeluaran>? = null
)
data class Pengeluaran (var jumlah : Long = 0,
var keterangan : String = "")
data class Pemasukan (var jumlah : Long = 0,
var keterangan : String ="")