How to access a TextView from a non-parent file [Android] - android

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.

Related

Firebase return null with mvvm

When I use the code below I can get data from firebase but when I want to access it with MVVM it returns null.
database.collection("Order")
.get()
.addOnCompleteListener { it ->
if (it.isSuccessful) {
val itemName = it.result.documents[0].data?.get("itemName")
val id = it.result.documents[0].data?.get("id")
It returns null inside Order.kt. I don't realize what the problem is there. I can't find any similar questions here.
FirebaseOrderService.kt
object FirebaseOrderService {
private const val TAG = "FirebaseOrderService"
suspend fun getOrderData(): Order? {
val db = FirebaseFirestore.getInstance()
return try {
db.collection("Order")
.document().get().await().toOrder()
} catch (e: Exception) {
Log.e(TAG, "Error getting order details", e)
FirebaseCrashlytics.getInstance().log("Error getting order details")
FirebaseCrashlytics.getInstance().setCustomKey("id", "1")
FirebaseCrashlytics.getInstance().recordException(e)
null
}
}
SuccessShoppingViewModel.kt
class SuccessShoppingViewModel: ViewModel() {
private val _orderList = MutableLiveData<Order>()
val order: LiveData<Order> = _orderList
init {
viewModelScope.launch {
_orderList.value = FirebaseOrderService.getOrderData()
_orderList
}
}
Order.kt
#Parcelize
data class Order(
val id: String = "",
val picUrl: String = "",
val itemName: String = "",
val itemPrice: Double = 0.0,
val itemAmount: String = "",
val itemQuantatiy: Int = 0
) : Parcelable {
companion object {
fun DocumentSnapshot.toOrder(): Order? {
return try {
val id = getString("id")!!
val picUrl = getString("picUrl")!!
val itemName = getString("itemName")!!
val itemPrice = getLong("itemPrice")?.toDouble()!!
val itemAmount = getString("itemAmount")!!
val itemQuantatiy = getLong("itemQuantatiy")?.toInt()!!
Order(id, picUrl, itemName, itemPrice, itemAmount, itemQuantatiy)
} catch (e: Exception) {
Log.e(TAG, "Error converting order", e)
FirebaseCrashlytics.getInstance().log("Error converting order")
FirebaseCrashlytics.getInstance().setCustomKey("id", id)
FirebaseCrashlytics.getInstance().recordException(e)
null
}
}
private const val TAG = "Order"
}
}
You're getting null because of the following line of code:
db.collection("Order")
.document().get().await().toOrder()
When you are using the above line of code, it means that you are creating a reference to a document with a random ID. Calling CollectionReferenc#document() method, without passing any arguments:
Returns a DocumentReference pointing to a new document with an auto-generated ID within this collection.
So what you're actually doing, you're creating a reference that points to a document that doesn't exist. To solve this problem, you have to pass the ID of the document to the document() function like this:
db.collection("Order")
.document("eBW6...zIO1").get().await().toOrder()
// 👆

How to test ViewModel + Flow

I'm doing a small project to learn flow and the latest Android features, and I'm currently facing the viewModel's testing, which I don't know if I'm performing correctly. can you help me with it?
Currently, I am using a use case to call the repository which calls a remote data source that gets from an API service a list of strings.
I have created a State to control the values in the view model:
data class StringItemsState(
val isLoading: Boolean = false,
val items: List<String> = emptyList(),
val error: String = ""
)
and the flow:
private val stringItemsState = StringtemsState()
private val _stateFlow = MutableStateFlow(stringItemsState)
val stateFlow = _stateFlow.asStateFlow()
and finally the method that performs all the logic in the viewModel:
fun fetchStringItems() {
try {
_stateFlow.value = stringItemsState.copy(isLoading = true)
viewModelScope.launch(Dispatchers.IO) {
val result = getStringItemsUseCase.execute()
if (result.isEmpty()) {
_stateFlow.value = stringItemsState
} else {
_stateFlow.value = stringItemsState.copy(items = result)
}
}
} catch (e: Exception) {
e.localizedMessage?.let {
_stateFlow.value = stringItemsState.copy(error = it)
}
}
}
I am trying to perform the test following the What / Where / Then pattern, but the result is always an empty list and the assert verification always fails:
private val stringItems = listOf<String>("A", "B", "C")
#Test
fun `get string items - not empty`() = runBlocking {
// What
coEvery {
useCase.execute()
} returns stringItems
// Where
viewModel.fetchStringItems()
// Then
assert(viewModel.stateFlow.value.items == stringItems)
coVerify(exactly = 1) { viewModel.fetchStringItems() }
}
Can someone help me and tell me if I am doing it correctly? Thanks.

Why is my adapter is not changing the value of TextView when I update the firestore database?

Model Class (Data Class)
data class Message(var userID:String? = "",var message:String? = "",var recID:String? = "",var isSeen:String="")
I have an an adapter that is supposed to change the value in a particular TextView based on the value of the isSeen field in my document. However this change does not happen, I thought that the adapter would make the change automatically when i call the notifyDataSetChanged() but since it did not, I added some event listener, listener registration and yet the TextView doesn't change when the isSeen changes from false to true. It only displays what it should display when the field is false.
onBindViewHolder inside Adapter Class
override fun onBindViewHolder(holder: MessageViewHolder, position: Int) {
val message:Message = MessageList[position]
if(position==MessageList.size-1){
if(message.isSeen=="true")
{
holder.textSeen.text = "Seen"
}else if(message.isSeen=="false"){
holder.textSeen.text = "Delivered"
}
}else{
holder.textSeen.visibility = View.GONE
}
}
Activity Class (where the value inside database successfully changes from false to true)
eventListener = EventListener { snapshot, _ ->
if (snapshot != null) {
val message: Message? = snapshot.toObject(Message::class.java)
if (message != null) {
if (message.recID.equals(userID) && message.userID.equals(recID)) {
val hashMap: HashMap<String, Any> = HashMap()
hashMap["isSeen"] = "true"
message.isSeen = "true"
snapshot.reference.update(hashMap)
messageAdapter.notifyDataSetChanged()
}
}
messageAdapter.notifyDataSetChanged()
}
}
private fun checkSeen() {
FirebaseFirestore.getInstance().collection("chats").document(roomID).collection("messages")
.addSnapshotListener(EventListener { results, e ->
if (e != null) {
Log.w(TAG, "Listen failed.", e)
return#EventListener
}
for (doc in results!!) {
val message: Message = doc.toObject(Message::class.java)
if (message.recID.equals(userID) && message.userID.equals(recID)) {
val hashMap: HashMap<String, Any> = HashMap()
hashMap["isSeen"] = "true"
message.isSeen = "true"
doc.reference.update(hashMap)
messageAdapter.notifyDataSetChanged()
}
}
})
messageAdapter.notifyDataSetChanged()
}
After Receiver opens the message
But for sender it still shows Delivered and not Seen.
try this code
if(position==MessageList.size-1){
if(message.isSeen=="true")
{
holder.textSeen.text = "Seen"
}else if(message.isSeen=="false"){
holder.textSeen.text = "Delivered"
}
holder.textSeen.visibility = View.VISIBLE <-- add this line
}else{
holder.textSeen.visibility = View.GONE
}

sending a value outside a thread

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)

Kotlin returns object with unassigned properties after assigning them in apply function

I'm trying to do a quite simple task: assign properties to an object and return that same object after retrieving the infos with a REST call.
In my runBlocking block I use the apply function to change the properties of my object, but after trying different ways to assign them, instantiate the object itself, modifying constructing logic of the object, I still get an object with the default values.
Here's my Info object:
class DrivingLicenceInfo {
var type : String = ""
var nationality : String = ""
var number : String = ""
var releaseDate : String = ""
var expiryDate : String = ""
}
Here's the method which gives me problems:
private fun getDerivingLicenceInfoAndWaitForCompletion(): DrivingLicenceInfo {
return runBlocking {
val response = retrieveDrivingLicenceInfoAsync().await()
if (response.isSuccessful) {
var info = DrivingLicenceInfo()
response.body()?.let {
info.apply {
it.data.let { data ->
val type = data.guy
val drivingLicenseNationality = data.drivingLicenseNationality
val drivingLicenseNumber = data.drivingLicenseNumber
val drivingReleaseDate = data.drivingReleaseDate
val drivingExpiryDate = data.drivingExpiryDate
this.type = type
this.nationality = drivingLicenseNationality
this.number = drivingLicenseNumber
this.releaseDate = drivingReleaseDate
this.expiryDate = drivingExpiryDate
}
}
info
Log.i("driving.info.call", info.type)
}
}
DrivingLicenceInfo()
}
}
And here's where I use it, in my Main, and where I get an info object with empty strings as properties
private void getDrivingLicenceData() {
DrivingLicenceInfoService service = new DrivingLicenceInfoServiceImpl(context);
DrivingLicenceInfo info = service.getDrivingLicenceInfo();
Log.i("driving.info.main",info.getType());
profileViewModel.licenceNumber.postValue(info.getNumber());
profileViewModel.licenceExpiryDate.postValue(info.getExpiryDate());
}
The log in the runBlocking correctly shows the property, the log in my Main doesn't even show up.
Using the debugger I am able to see that info has empty strings as value.
Could somebody help me to figure out what I'm doing wrong?
Thank you
Beside #JeelVankhede giving you the main reason for your problem, I suggest some minor code improvements as well. I personally feel this is ways less verbose and better readable
private fun getDrivingLicenceInfoAndWaitForCompletion(): DrivingLicenceInfo {
return runBlocking {
val response = retrieveDrivingLicenceInfoAsync().await()
var info = DrivingLicenceInfo()
return if (response.isSuccessful) {
response.body()?.let {
info.apply {
type = it.data.guy
nationality = it.data.drivingLicenseNationality
number = it.data.drivingLicenseNumber
releaseDate = it.data.drivingReleaseDate
expiryDate = it.data.drivingExpiryDate
}
Log.i("driving.info.call", info.type)
info
} ?: info
} else { info }
}
}
Since #JeelVankhede already told you the main reason of your problem and I also have some suggestions apart from the one given by #WarrenFaith.
If DrivingLicenceInfo is a model class you can declare it as data class like
data class DrivingLicenceInfo (
val type : String = "",
val nationality : String = "",
val number : String = "",
val releaseDate : String = "",
val expiryDate : String = ""
)
you can read more about data class here.
And then you can write your function as
private fun getDerivingLicenceInfoAndWaitForCompletion(): DrivingLicenceInfo {
val info = runBlocking {
val response = retrieveDrivingLicenceInfoAsync().await()
if (response.isSuccessful) {
response.body()?.let {
it.data.let { data ->
DrivingLicenceInfo(
type = data.guy,
nationality = data.drivingLicenseNationality,
number = data.drivingLicenseNumber,
releaseDate = data.drivingReleaseDate,
expiryDate = data.drivingExpiryDate
)
}
} ?: DrivingLicenceInfo()
} else {
DrivingLicenceInfo()
}
}
Log.i("driving.info.call", info.type)
return info
}

Categories

Resources