Get price in-app Billing Library - kotlin

My apk is uploaded in the alpha channel, my products created, the buy button if it works.
I'm trying to show several products to buy in a RecyclerView. The purchases work for me. What I could not do is show the price and title of the products.
In my myadapter.kt file I have the following var var p = ArrayList<String>() and function:
fun queryskudetails() {
billingClient = BillingClient.newBuilder(context).setListener(this).build()
billingClient.startConnection(object : BillingClientStateListener {
override fun onBillingServiceDisconnected() {
Log.i("Disconnected", "billing client")
}
override fun onBillingSetupFinished(responseCode: Int) {
billingClient.let { billingClient ->
val skulist = ArrayList<String>()
skulist.add("books")
skulist.add("pens")
skulist.add("keychains")
val params = SkuDetailsParams.newBuilder()
params.setSkusList(skulist).setType(BillingClient.SkuType.INAPP)
billingClient.querySkuDetailsAsync(params.build(), { responseCode, skuDetailsList ->
if (responseCode == BillingClient.BillingResponse.OK && skuDetailsList != null) {
for (skuDetails in skuDetailsList) {
val sku = skuDetails.sku
val price = skuDetails.price
Log.i("skudetails", sku)
Log.i("skuprice", price)
hashMap[sku] = price
println("===== price and sku ======")
println(price)
println(sku)
println("===== /proce and sku ======")
// add price to array p1 (defined as a global variable)
p1.add(price)
}
p = precios
}
})
}
}
})
}
In the section onBindViewHolder which is where I assign the price and title to a textView:
override fun onBindViewHolder(holder: Vholder, position: Int) {
queryskudetails()
print("-----Here array price print [] -----")
println (p)
var text: String = array[position]
Log.i("text", text)
holder.textView.text = text
holder.Price.text = hashMap[text.toLowerCase()].toString() // this does not work for me, retun null
Log.i("price", hashMap["books"].toString())
println(hashMap[array[position]]) // retunr null
holder.btn.setOnClickListener(View.OnClickListener {
Log.i("button", text.toLowerCase())
var skuid = hashMap2[text]
val flowParams = BillingFlowParams.newBuilder()
.setSku(text.toLowerCase())
.setType(BillingClient.SkuType.INAPP)
.build()
val responseCode = billingClient.launchBillingFlow(context as Activity?, flowParams)
})
}
When I show the price in a textview the following code does not work for me:
holder.Price.text = hashMap[text.toLowerCase()].toString() where Price is var Price: TextView = itemView.findViewById(R.id.price)
As a second option I try to use the matrix p1 where I stored all the prices in thequeryskudetails ()function but it returns empty.
How can I do to use the content of the array p1?

The price is stored in the Map: hashMap, to recover it use the sku (identifier in google play console)
hashMap = {sku1=USD 3.99, sku2=USD 1.99, sku3=USD 3.99}
//to recover the values according to the sku (key)
hashMap[sku1] = USD 3.99
hashMap[sku2] = USD 1.99
As I see in your code holder.Price.text = hashMap[text.toLowerCase()].toString() (in the variable Text you must have the identifier (identifier = sku) in order to recover the price of each product ), it is correct, check that in another part is not making conflict or has repeated.

Related

MPhiljay Barchart in android

In Barchart show x values from column belongs to one label more than 2 two but should be one.
viewModel.reportMounthData.observe(this, Observer { reportModelList->
val barChart = binding.idBarChartMounth
barChart.setScaleEnabled(false)
val entries: ArrayList<BarEntry> = ArrayList()
var i=0
reportModelList.forEach { reportModel->
entries.add(BarEntry(reportModel.YigilganBonus.toFloat(), i))
i++
}
val bardataset = BarDataSet(entries, "Oylik maoshlar miqdori")
val labels = ArrayList<String>()
reportModelList.sortedBy { it.oy }.forEach { reportModel ->
labels.add(mounthSalary(reportModel.oy))
}
val data = BarData(labels, bardataset)
barChart.data = data // set the data and list of labels into chart
barChart.setDescription("\uD83D\uDEE0 Oylik maosh statistikasi") // set the description
// bardataset.setColors(ColorTemplate.COLORFUL_COLORS)
barChart.animateY(2500)
})
enter image description here
it works perfectly until the values ​​reach 6. If there are more than 6, a defect may occur naturally. they said that the main reason for this is that it will not fit on the screen

How do I assign a value to an empty string in my code to prevent NumberFormatException

I'm having a NumberFormatException which is likely due passing an empty string to product_quantity in the product and cart data class. I'm having difficulty assigning value to the product_quantity in my code before parsing it toInt()
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.hardextech.store, PID: 22948
java.lang.NumberFormatException: For input string: ""
at java.lang.Integer.parseInt(Integer.java:627)
at java.lang.Integer.parseInt(Integer.java:650)
at com.hardextech.store.ui.activities.ui.activities.CartListActivity.successfullyGetTheCartItemList(CartListActivity.kt:90)
at com.hardextech.store.firestore.FirestoreClass.getCartList$lambda-28(FirestoreClass.kt:493)
at com.hardextech.store.firestore.FirestoreClass.$r8$lambda$Uc6EU1OaDQc7HeKgs7cM5uEsC2A(Unknown Source:0)
at com.hardextech.store.firestore.FirestoreClass$$ExternalSyntheticLambda12.onSuccess(Unknown Source:4)
at com.google.android.gms.tasks.zzn.run(com.google.android.gms:play-services-tasks##17.2.0:4)
at android.os.Handler.handleCallback(Handler.java:873)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6819)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:497)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:912)
This is the successfullyGetCartTheCartItemList method referenced from the debugging console
fun successfullyGetTheCartItemList(listOfItemInTheCart: ArrayList<Cart>){
dismissProgressDialogue()
// checking for the product list in the stock-- determining if the product is available in the stock
for (everyProductInTheProductList in mProductList){
for (everyProductInTheCart in listOfItemInTheCart){
if (everyProductInTheProductList.product_id == everyProductInTheCart.product_id){
everyProductInTheCart.product_quantity = everyProductInTheProductList.product_quantity
// if there are no products in the stock
if (everyProductInTheProductList.product_quantity.toInt() == 0){
everyProductInTheCart.cart_quantity = everyProductInTheProductList.product_quantity
}
}
}
}
// initializing the mCartDataClassDetails
mCartDataClassDetails = listOfItemInTheCart
// checking for the product list in the cart
if (mCartDataClassDetails.size >0){
rv_cart_item_list.visibility = View.VISIBLE
ll_check_out.visibility = View.VISIBLE
tv_no_cart_item_found.visibility = View.GONE
rv_cart_item_list.layoutManager = LinearLayoutManager(this)
rv_cart_item_list.setHasFixedSize(true)
// create an adapter variable for the recycler view
val cartListAdapter = CartItemListAdapter(this, listOfItemInTheCart)
// pass the adapter to the recyclerview
rv_cart_item_list.adapter = cartListAdapter
// assign double to the sub-total in the itemListCart
var subTotal: Double = 0.0
// run through all product in the list
for (everyItemInTheCart in mCartDataClassDetails){
// checking for the available quantity of product
val availableProductQuantity = everyItemInTheCart.product_quantity.toInt()
if (availableProductQuantity > 0){
Log.i("Cart Title", everyItemInTheCart.product_title)
// convert the price to Double
val price = everyItemInTheCart.product_price.toDouble()
// convert the quantity to Int
val quantity = everyItemInTheCart.cart_quantity.toInt()
// calculate the sub-total cost
subTotal+=(price*quantity)
}
}
// assign the value of the sub total for each product sales
tv_product_subTotal.text = "NGN $subTotal"
// assigning the delivery cost for each product sales
// TODO: Seek for API to calculate the delivery cost for each product and/or write the code criteria for calculating it
tv_delivery_cost.text = (subTotal*0.05).toString()
if (subTotal > 0){
ll_check_out.visibility = View.VISIBLE
// TODO: Change the logic for the delivery cost
val totalProductCost = subTotal + (subTotal*0.05)
tv_total_product_cost.text = "NGN $totalProductCost"
} else{
ll_check_out.visibility = View.GONE
}
} else{
rv_cart_item_list.visibility = View.GONE
ll_check_out.visibility = View.GONE
tv_no_cart_item_found.visibility = View.VISIBLE
}
}
This is method from the Firestore class
fun getCartList(activity: Activity){
// the method for downloading the cart item list
mFireStore.collection(Constants.CART_ITEMS_COLLECTION)
// get the cart items for the logged in user
.whereEqualTo(Constants.USER_ID, getCurrentUserID())
.get()
.addOnSuccessListener { document->
Log.i(activity.javaClass.simpleName, document.documents.toString())
// get the list of the cart items--- number of products in the cart
val listOfCartItems: ArrayList<Cart> = ArrayList()
// run through each of the document(FireStore document field) in the list
for (loopingThroughCartItems in document.documents){
// converting the products in the cart to an object and surround with null check
val cartItemListObject = loopingThroughCartItems.toObject(Cart::class.java)!!
cartItemListObject.id= loopingThroughCartItems.id
// add the result of the loop to the cart item arrayList
listOfCartItems.add(cartItemListObject)
}
when(activity){
is CartListActivity->{
activity.successfullyGetTheCartItemList(listOfCartItems)
}
}
}.addOnFailureListener {e->
Log.e(activity.javaClass.simpleName, " Error downloading products in the cart", e)
when(activity){
is CartListActivity->{
activity.dismissProgressDialogue()
}
}
}
}
This is Product Data class
#Parcelize
data class Product(
val user_id: String = "",
var id: String = "",
val product_image: String = "",
val product_title: String = "",
val product_price: String = "",
val product_description: String = "",
val product_quantity: String = "",
val user_name: String = "",
var product_id:String =""
): Parcelable
This is the cart data class
#kotlinx.parcelize.Parcelize
data class Cart(
val user_id: String = "",
val product_id: String = "",
val product_title: String = "",
val product_image: String = "",
val product_price: String = "",
var cart_quantity: String = "",
var product_quantity: String ="",
var id: String = ""
): Parcelable
You can use toIntOrNull, which returns parsed Int or null when failed to parse. Then, use let with ?., like this:
string.toIntOrNull()?.let { parsedInt ->
// Your code here
}

How to combine Flux and Mono

Assume we have the following function
fun getAnnualData(tenant: String): Flux<DashboardResponse> {
val year = LocalDate.now().year
val annualExpenses = expenseFinder.sumAllByYearAndTenant(year = year, tenant = tenant)
val warehouseExpenses = expenseFinder.sumWarehouseByYearAndTenant(year = year, tenant = tenant)
val annualRevenues = revenueFinder.sumAllByYearAndTenant(year = year, tenant = tenant)
return annualExpenses.zipWith(annualRevenues)
.filter { it.t1._id?.year == year }
.filter { it.t2._id?.year == year }
.map {
DashboardResponse(
period = Period.ANNUAL,
expenses = it.t1,
revenue = it.t2
)
}
}
and I want to add the warehouseExpenses to the returning value. How would I do that?
Where
annualExpenses = Flux
warehouseExpenses = Mono
annualRevenue = Flux
You can simply use a flatMap to combine Mono:
Flux.just(...).zipWith(Flux.just(...))
.filter(...)
.flatMap(tuple -> Mono.just(true).map(m -> new DashboardResponse(tuple.getT1(), tuple.getT2(), m)));

Kotlin ListView IndexOutOfBoundsException

(new to kotlin) I'm making my own music app but i'm having an error that i don't understand :
in the bit of code where i try to access a random view (shuffle), i get the java.lang.IndexOutOfBoundsException: Index:96 Size:11.
96 in this example is the view in the listview that i'm trying to access.
It happens in this line : var iView = listViewMusic.get(idShuffle)
Same if i use listViewMusic[idShuffle]
EDIT : Turns out that 11 is only the 11 visible items on screen at any given moment, even if the list contains hundreds of items. When i use listViewMusic.smoothscrolltoposition(idShuffle) it works, but the size of 11 now relates to the 11 on screen after the scrolling
The function playNext() inside the activity, called when clicking on the shuffle button:
fun playNext(){
try {
// Find the order of the next song to play
var idShuffle = musicAdapter!!.getIdofItem(listMusicShuffled.get(musicNextToPlay).title)
// Find the right record in the list
//var iView = listViewMusic[idShuffle]
//toastIt(applicationContext, "adapter count ${listViewMusic.adapter.count}")
//toastIt(applicationContext, "listview count ${listViewMusic.count}")
var iView = listViewMusic.get(idShuffle) //throws the error
// Play it
playOrPause(iView)
// Prepare the next track
musicNextToPlay += 1
if (musicNextToPlay >= listMusicShuffled.size) {
musicNextToPlay = -1
}
} catch (e:Exception) {toastException(applicationContext, "playnext", e) }
}
The part of the function onCreate that fills in the listViewMusic:
// Retrieve the data
val mediaStoreUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI
val cursor = contentResolver.query(mediaStoreUri, null, "", null, null)
//Browse the data
listMusic = mutableListOf()
val listMusicJson = getMusicFromJson()
while (cursor!!.moveToNext()) {
val musicName = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME))
val musicArtist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST))
val musicUrl = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA))
val musicType:String =
if (musicArtist.contains("lmdmf", true)) { "LMDMF" }
else if (musicName.contains("slack", true)) { "SLACK" }
else { "MUSIC" }
listMusic.add(
MusicTrack(
musicName,
musicPlayCount,
musicUrl,
musicType
)
)
}
cursor.close()
musicAdapter = MusicAdapter(listMusic.sortedWith(compareBy<MusicTrack>{ it.title }).filter { it.type == "MUSIC"}.toMutableList())
listViewMusic.adapter = musicAdapter

Compare multiple fields of Object to those in an ArrayList of Objects

I have created a 'SiteObject' which includes the following fields:
data class SiteObject(
//Site entry fields (10 fields)
var siteReference: String = "",
var siteAddress: String = "",
var sitePhoneNumber: String = "",
var siteEmail: String = "",
var invoiceAddress: String = "",
var invoicePhoneNumber: String = "",
var invoiceEmail: String = "",
var website: String = "",
var companyNumber: String = "",
var vatNumber: String = "",
)
I want to filter an ArrayList<SiteObject> (call it allSites) by checking if any of the fields of the objects within the list match those in a specific <SiteObject> (call it currentSite).
So for example, I know how to filter looking at one field:
fun checkIfExistingSite(currentSite: SiteObject) : ArrayList<SiteObject> {
var matchingSites = ArrayList<SiteObject>()
allSites.value?.filter { site ->
site.siteReference.contains(currentSite.siteReference)}?.let { matchingSites.addAll(it)
}
return matchingSites
}
But I am looking for an elegant way to create a list where I compare the matching fields in each of the objects in allSites with the corresponding fields in currentSite..
This will give me a list of sites that may be the same (allowing for differences in the way user inputs data) which I can present to the user to check.
Use equals property of Data Class:
val matchingSites: List<SiteObject> = allSites
.filterNotNull()
.filter { it.equals(currentSite) }
If you are looking for a more loose equlity criteria than the full match of all fields values, I would suggest usage of reflection (note that this approach could have performance penalties):
val memberProperties = SiteObject::class.memberProperties
val minMatchingProperties = 9 //or whatever number that makes sense in you case
val matchingItems = allSites.filter {
memberProperties.atLeast(minMatchingProperties) { property -> property.get(it) == property.get(currentSite) }
}
fun <E> Iterable<E>.atLeast(n: Int, predicate: (E) -> Boolean): Boolean {
val size = count()
return when {
n == 1 -> this.any(predicate)
n == size -> this.all(predicate)
n > size - n + 1 -> this.atLeast(size - n + 1) { !predicate.invoke(it) }
else -> {
var count = 0
for (element in this) {
if (predicate.invoke(element)) count++
if (count >= n) return true
}
return false
}
}
}
you could specify all the fields by which you want to match the currentSite inside the filter predicate:
fun checkIfExistingSite(currentSite: SiteObject) =
allSites.filter {
it.siteAddress == currentSite.siteAddress
|| it.sitePhoneNumber == currentSite.sitePhoneNumber
|| it.siteReference == currentSite.siteReference
}
Long but fast solution because of short circuiting.
If the list is nullable you can transform it to a non nullable list like:
allSites?filter{...}.orEmpty()
// or imho better
allSites.orEmpty().filter{...}