How to use the information stored in mutableStateOf in Jetpack Compose - kotlin

I have information in json and I retrieve it using retrofit2, everything works fine, I get the data in a List.
I need this information to fill elements in Jetpack Compose for which I use mutableStateOf to save the states.
My function that I use is the following:
fun jsonParsing(
dataRecox: MutableState<List<Event>>
) {
val TAG_LOGS = "Mariox"
val retrofit = Retrofit.Builder()
.baseUrl("http://myserversample.com/pGet/track/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitAPI = retrofit.create(APIService1::class.java)
retrofitAPI.getRecolector().enqueue(object : Callback<List<Event>> {
override fun onResponse(
call: Call<List<Event>>,
response: Response<List<Event>>
) {
val data = response.body()
val mydata = data!!
dataRecox.value = mydata
Log.i(TAG_LOGS, Gson().toJson(data))
}
override fun onFailure(call: Call<List<Event>>, t: Throwable) {
t.printStackTrace()
}
})
}
Mymodel:
data class Event (
val deviceID : Int,
val statusCode : Int,
val accountID : String,
val speedKPH : Int,
.
.
.
}
My composable:
#Composable
fun Greeting(name: String) {
val dataRecox = remember {
mutableStateOf(emptyList<Event>())
}
jsonParsing(dataRecox)
println("======")
println(dataRecox) // ok data
println(dataRecox.value). // ok data
//Uncommenting println(dataRecox.value[0]) I get empty.
//println(dataRecox.value[0])
//Text(text = dataRecox.value[0].uniqueID)
}
When I do not use the information in the console, by calling Greeting("Android") all the data is printed correctly:
The problem comes when I want to use that information:
For example, if I want to print in console println(dataRecox.value[0]) here it returns empty. If I want to use it with a composable Text: Text(text = dataRecox.value[0].uniqueID) it also gives me empty.
Can someone explain to me why this happens, because when I start using the information the data becomes empty.

The way you're doing is totally different of the recommended way... here's my suggestion.
Define a class to represent the screen's state.
data class ScreenState(
val events: List<Event> = emptyList(),
val error: Throwable? = null
)
Use a ViewModel to perform the API request and keep the screen state.
class EventsViewModel : ViewModel()
private val _screenState = MutableStateFlow<ScreenState>(ScreenState())
val screenState = _screenState.asStateFlow()
init {
jsonParsing()
}
fun jsonParsing() {
val TAG_LOGS = "Mariox"
val retrofit = Retrofit.Builder()
.baseUrl("http://myserversample.com/pGet/track/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitAPI = retrofit.create(APIService1::class.java)
retrofitAPI.getRecolector().enqueue(object : Callback<List<Event>> {
override fun onResponse(
call: Call<List<Event>>,
response: Response<List<Event>>
) {
val data = response.body()
Log.i(TAG_LOGS, Gson().toJson(data))
_screenState.update {
ScreenState(it.events)
}
}
override fun onFailure(call: Call<List<Event>>, t: Throwable) {
t.printStackTrace()
_screenState.update {
ScreenState(error = t)
}
}
})
}
}
Instantiate the ViewModel and use it in your screen...
#Composable
fun Greeting(name: String) {
val vm = viewModel<EventsViewModel>()
val screenState by vm.screenState.observeAsState()
LazyColumn(Modifier.fillMaxSize()) {
items(screenState.items) {
Text(it. accountID)
}
}
}

Related

Struggling to access Spinner outside of my recycler view

I have tried two different ways to access my spinner. Without success thus far.
I want to load the data for each driver as chosen.
To give an idea of my app.
Code for adapter:
class TableViewAdapter(var tripsheetlist: Tripsheetlist) : RecyclerView.Adapter<TableViewAdapter.RowViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RowViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.table_list_item, parent, false)
return RowViewHolder(itemView) }
override fun getItemCount(): Int { return tripsheetlist.videos.size + 1 // one more to add header row
}
override fun onBindViewHolder(holder: RowViewHolder, position: Int) {
val rowPos = holder.adapterPosition
if (rowPos == 0) {
// Header Cells. Main Headings appear here
holder.itemView.apply {
setHeaderBg(txtWOrder)
setHeaderBg(txtDElNote)
setHeaderBg(txtCompany)
// setHeaderBg(txtAddress)
setHeaderBg(txtWeight)
setHeaderBg(txtbutton1)
setHeaderBg(txtbutton2)
setHeaderBg(txttvdone)
txtWOrder.text = "WOrder"
txtDElNote.text = "DElNote"
txtCompany.text = "Company"
// txtAddress.text = "Address"
txtWeight.text = "Weight"
txtbutton1.text = "Delivered"
txtbutton2.text = "Exception"
txttvdone.text = ""
}
} else {
val modal = tripsheetlist.videos[rowPos -1]
holder.itemView.apply {
setContentBg(txtWOrder)
setContentBg(txtDElNote)
setContentBg(txtCompany)
setContentBg(txtWeight)
setContentBg(txtbutton1)
setContentBg(txtbutton2)
setContentBg(txttvdone)
val list : MutableList<String> = ArrayList()
list.add("Deon")
list.add("Leon")
list.add("David")
list.add("Dick")
println(list)
val spinner : Spinner = findViewById(R.id.spnDriver)
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
val item :String = list[p2]
if (item == "David")
{
txtWOrder.text = modal.WOrder.toString()
txtDElNote.text = modal.DElNote.toString()
txtCompany.text = modal.name.toString()
txtWeight.text = modal.id.toString()
}
}
override fun onNothingSelected(p0: AdapterView<*>?) {
}
}
I did it like this as a test for now. As I will get the drivers from my JSON. I don't have access to it yet so that is why the static values.
The problem I am getting now is: findViewById(R.id.spnDriver) must not be null
I first had my spinner class in my main activity and passed it over like this:
val list : MutableList<String> = ArrayList()
list.add("Deon")
list.add("Leon")
list.add("David")
list.add("Dick")
list.add("Jim")
list.add("Harry")
val adapter = ArrayAdapter( this, androidx.appcompat.R.layout.support_simple_spinner_dropdown_item, list)
val spinner: Spinner = findViewById(R.id.spnDriver)
spinner.adapter = adapter
spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
val item :String = list[p2]
Toast.makeText(this#MainActivity, "Driver $item selected", Toast.LENGTH_SHORT).show()
}
override fun onNothingSelected(p0: AdapterView<*>?) {
//empty
}
// insert code that activates data pull of tripsheet for driver= actifavte by method the class/object that activates the data pull. so datapuul(Driver)
}
limitDropDownHeight(spinner)
//drivers end
val btnLoadData: Button = findViewById(R.id.btnLoadData)
// weightsum(tvTotalweight, Tripsheetlist)
// totaldelNotes(tvTotaldelv,Tripsheetlist)
// setData(btnLoadData, Tripsheetlist )
fetchJson(spinner)
}
private fun fetchJson(spinner: Spinner) {
println("Attempting to Fetch JSON")
val url = "https://api.letsbuildthatapp.com/youtube/home_feed"
val request = Request.Builder().url(url).build()
val client = OkHttpClient()
client.newCall(request).enqueue(object: Callback {
override fun onFailure(call: Call, e: IOException) {
println("Failed to execute request") }
override fun onResponse(call: Call, response: Response) {
val body = response.body?.string()
println(body)
val gson = GsonBuilder().create()
val tripsheetlist = gson.fromJson(body, Tripsheetlist::class.java)
runOnUiThread {
recyclerViewTripsheetlist.adapter = TableViewAdapter(tripsheetlist, spinner)
}
}
})
}
In my Adapter class I then called it with : val spinner = spnDriver
This led to a different error: AppCompatSpinner.setOnItemSelectedListener(android.widget.AdapterView$OnItemSelectedListener)' on a null object reference
But seems like it passed the val spinner =spnDriver without a problem.
Thank you for all input and help.
I found a solution. What I did was to keep the spinner inside my MainActivity and then just pass the result of the spinner to the adapter - where I wanted to use it.

Not able to return a desired value inside a overidden method using coroutines in kotlin

I am new to kotlin and coroutines.I have been working on a client-server part of an android app.I am using mediasoup-client-android library for this.
I am trying to intialize the sendtransports using createSendTransport() method.This method does have a sendTransportListener which has abstract methods.One of them being is onProduce() which returns a producerId which is a String. awaitEmit() is an asynchronous action and I need this in onProduce().To use awaitEmit() I used coroutines.But I need String as return type instead of Deferred.Is there any other way to implement the mentioned logic? Below is my code
class RoomClient {
suspend fun initTransports(device: Device) {
coroutineScope {
val id: String?
val iceParameters: String?
val iceCandidates: String?
val dtlsParameters: String?
val sctpParameters: String?
try {
val params = JSONObject()
params.put("forceTcp",false)
params.put("rtpCapabilities", this#RoomClient.device?.rtpCapabilities)
val res = socket?.awaitEmit("createWebRtcTransport",params)
val data = res?.get(0) as JSONObject
if (data.has("error")) {
Log.d(TAG, data.getString("error"))
return#coroutineScope
}
id = data.optString("id")
iceParameters = data.optString("iceParameters")
iceCandidates = data.optString("iceCandidates")
dtlsParameters = data.optString("dtlsParameters")
sctpParameters = data.optString("sctpParameters")
} catch (e: Throwable) {
Log.e(TAG, "${e.message}")
return#coroutineScope
}
val sendTransportListener: SendTransport.Listener = object : SendTransport.Listener {
private val listenerTAG = TAG.toString() + "_ProducerTrans"
override fun onProduce(
transport: Transport,
kind: String?,
rtpParameters: String?,
appData: String?
): String? {
this#coroutineScope.async{
var producerId: String? = null
Log.d(listenerTAG, "onProduce() ")
val producerDeferred = launch {
val params = JSONObject("""{"producerTransportId": transport.id, "kind": kind, "rtpParameters": rtpParameters,"appData": appData}""")
val res = socket?.awaitEmit("produce",params)
val data = res?.get(0) as JSONObject
producerId = data.getString("producer_Id")
}
producerDeferred.join()
Log.d(listenerTAG, "producerId inside the coroutine: $producerId"
}
return#async producerId
}
}
this#RoomClient.producerTransport = device.createSendTransport(
sendTransportListener, id,
iceParameters,
iceCandidates,
dtlsParameters
)
}
}
}
And also I am not sure about the way coroutines are used here.Please correct me If I have missed something central.

Kotlin: How to fetch data from web service and display it in app?

Hello :) I'm working on app that shows popular movies and some details about each of them. I created a RecyclerView where information should be displayed. I'm stuck with getting and displaying data. I'm using https://www.themoviedb.org/ page for api.
I was following these steps: http://imakeanapp.com/make-a-movies-app-using-tmdb-api-part-4-networking-using-retrofit-library/ , but it's written in Java and I need code in Kotlin. I converted by myself a part of the code, here is what I have:
in Movie.kt
data class Movie (
#SerializedName("id") val id: Int,
#SerializedName("title") val title: String,
#SerializedName("poster_path") val posterPath: String,
#SerializedName("release_date") val releaseDate: String,
#SerializedName("vote_average") val rating: Float
)
in MoviesResponse.kt
data class MoviesResponse (
#SerializedName("page") val page: Int,
#SerializedName("total_results") val totalResults: Int,
#SerializedName("results") val movies: List<Movie>,
#SerializedName("total_pages") val totalPages: Int
)
in TMDbApi.kt
interface TMDbApi {
#GET("movie/popular")
fun getPopularMovies (
#Query("api_key") apiKey: String,
#Query("language") language: String,
#Query("page") page: Int
): Call<List<MoviesResponse>>
}
in OnGetMoviesCallback.kt
interface OnGetMoviesCallback {
fun onSuccess(movies: List<Movie>)
fun onError()
}
in MainAdapter.kt
class MainAdapter: RecyclerView.Adapter<CustomHolder>(){
var movies: List<Movie> = listOf()
fun MainAdapter(movies: List<Movie>) {
this.movies = movies
}
override fun getItemCount(): Int {
return movies.size
}
override fun onBindViewHolder(holder: CustomHolder, position: Int) {
holder.bind(movies.get(position))
holder?.setOnClick()
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CustomHolder {
val layoutInflater = LayoutInflater.from(parent?.context)
val cellForRow = layoutInflater.inflate(R.layout.movie_row, parent, false)
return CustomHolder(cellForRow)
}
}
class CustomHolder(view: View): RecyclerView.ViewHolder(view){
fun bind(result: Movie){
itemView.title.text = result.title
itemView.release_date.text = result.releaseDate
}
}
in MoviesRepository.kt
val BASE_URL: String = "https://api.themoviedb.org/3/"
val LANGUAGE: String = "en-US"
data class MoviesRepository (
val repositroy: MoviesRepository,
val api: TMDbApi
)
object getInstance{
val retrofit: TMDbApi = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.client(OkHttpClient().newBuilder().build())
.baseUrl(BASE_URL)
.build()
.create(TMDbApi::class.java)
}
in AllMoviesActivity.kt
class AllMoviesActivity : AppCompatActivity(), Callback<List<MoviesResponse>> {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_all_movies)
rw_main.layoutManager = LinearLayoutManager(this)
rw_main.adapter = MainAdapter()
getMovies()
}
private fun getMovies() {
getInstance.retrofit.getPopularMovies("2B0b0e8d104f0d6130a4fc67848f89e107", LANGUAGE, 1).enqueue(this)
}
override fun onResponse(call: retrofit2.Call<List<MoviesResponse>>, response: Response<List<MoviesResponse>>) {
val moviesResponse = response.body() ?: listOf()
Log.d("Results", moviesResponse.toString())
}
override fun onFailure(call: retrofit2.Call<List<MoviesResponse>>, t: Throwable) {
Toast.makeText(this, "Failed", Toast.LENGTH_SHORT).show()
}
}
And I have added in Manifest file Internet permission. When I run this, I get 'Failed' Toast and I don't see RecyclerView. Can you tell me what I'm missing or what's wrong? I've been searching on the Internet solutions but with no results. Hope you could help me. Thanks!
Looks like you are missing something small. After making a call to the API on Postman, Here's what you expect on your response body:
{
"page": 1,
"total_results": 10000,
"total_pages": 500,
"results": [...
}
Having brought that up, lets digest your lines, in TMDbApi.kt, you have indicated that your expected response is a List<MoviesResponse> which conflicts the response you receive as it is a JSON of type MoviesResponse,
Fix:
...): Call<List<MoviesResponse>>
}
to:
): Call<MoviesResponse>
}
The rest are small fixes to align your response handling.
Head over to AllMoviesActivity.kt and change the following:
class AllMoviesActivity : AppCompatActivity(), Callback<List<MoviesResponse>> {
to
class MainActivity : AppCompatActivity(), Callback<MoviesResponse>
Then on the callback functions, change signatures like so:
override fun onResponse(call: retrofit2.Call<List<MoviesResponse>>, response: Response<List<MoviesResponse>>) {
val moviesResponse = response.body() ?: listOf() ...
to:
override fun onResponse(call: Call<MoviesResponse>, response:Response<MoviesResponse>
) {
val moviesResponse = response.body()//type will be inferred ...
Then finally,
override fun onFailure(call: retrofit2.Call<List<MoviesResponse>>, t: Throwable) {
to:
override fun onFailure(call: Call<MoviesResponse>, t: Throwable) {
That's the basic to get the response and keep the Failure toast at bay.
Parting shot
Make the logs your friend, they can reveal the messy & smallest misses

how to get the data from an getParcelableArrayListExtra<Parcelable> in kotlin

How to get the data from an getParcelableArrayListExtra in kotlin?.
I have a Parceable data class called FoodParceable
data class FoodParceable(val idCover: Int, val name: String, val
price: Double):Parcelable {
constructor(parcel: Parcel) : this(
parcel.readInt(),
parcel.readString(),
parcel.readDouble()
)
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(idCover)
parcel.writeString(name)
parcel.writeDouble(price)
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<FoodParceable> {
override fun createFromParcel(parcel: Parcel): FoodParceable {
return FoodParceable(parcel)
}
override fun newArray(size: Int): Array<FoodParceable?> {
return arrayOfNulls(size)
}
}
}
In the current Acitvity A, fill in the arrayListParceable and send it to activity B.
val arrayListParceable = ArrayList<FoodParceable>()
for (Food in listFood) {
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Carne", 9.99))
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Vegetales", 29.99))
arrayListParceable.add(FoodParceable(R.mipmap.ic_food_meat, "Frutas", 39.99))
}
val intent = Intent()
intent.putParcelableArrayListExtra(LIST_PRODUCT,arrayListParceable)
activity?.setResult(Activity.RESULT_OK, intent)
When I get the value in activity B, in list item, I can not enter its content.
if(resultCode == Activity.RESULT_OK){
if(data != null){
val listItems = data.getParcelableArrayListExtra<Parcelable>(LIST_PRODUCT)
listItems[0]. //ERROR
}
}
I am new to kotlin, thank you for your comments. Regards
You have to give Kotlin more information about the type of your object list. Try this:
val listItems : ArrayList<FoodParceable> = data.getParcelableArrayListExtra(LIST_PRODUCT)

Not able to call Multiple API Parallely using Retrofit kotlin rxjava and how to call flatMap and zip together with Common Place error handling

What i want to achive is
Call IndexList API and display the responded list in a spinner -> Click on Spinner item i want to call 3 api in parallel using .zip operator and display in DetailViewModel
I am using flatmap for calling api in sequence
In first flatmap i am calling IndexList api and i am passing IndexList response to call 3 multiple api in parallel using Observable.zip operator but i am not able
to achive the second part of calling 3 API in parallel and display in view model
Code Snippet:
Spinner Click :
spinner_index_list?.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
//Performing action onItemSelected and onNothing selected
override fun onItemSelected(parent: AdapterView<*>, view: View, pos: Int, id: Long) {
mRecyclerView?.visibility = View.GONE
//Select item from spinner calling API
val symbolDefaultGroup = SymbolForDefaultGroup(indexExchangeSegmentList[pos], parent.getItemAtPosition(pos).toString())
grpIndexViewModel.getSearchSymbol(symbolDefaultGroup)?.observe(this#FragmentWatchlist, Observer { instrumentByIdResponse ->
mDataProvider = ExpandableDataProvider(instrumentByIdResponse)
mRecyclerView?.visibility = View.VISIBLE
if (instrumentByIdResponse != null)
initRecyclerView(savedInstanceState)
})
}
ViewModel :
fun getSearchSymbol(symbolForDefaultGroup: SymbolForDefaultGroup): LiveData<List<InstrumentByIdResponse>>? {
instrumentByIdResponse = null
instrumentByIdResponse = MutableLiveData<List<InstrumentByIdResponse>>()
instrumentByIdResponse = groupRepository.getSearchSymbol(symbolForDefaultGroup)
L.d("get symbol for default group method")
return instrumentByIdResponse
}
Repository:
override fun getSearchSymbol(symbolForDefaultGroup: SymbolForDefaultGroup): LiveData<List<InstrumentByIdResponse>> {
val mutableLiveData = MutableLiveData<List<InstrumentByIdResponse>>()
val instrumentsIdList = ArrayList<Instrument>()
val marketDataQuotesList = ArrayList<QuotesList>()
val subscriptionList = ArrayList<SubscriptionList>()
val symbolFroDefaultGroup: Observable<BaseResponse<SymbolForDefaultGroupResponse>> = remoteServices.requestSymbolForDefaultGroup(symbolForDefaultGroup)
symbolFroDefaultGroup
.flatMap { response ->
for (i in 0 until response.result!!.instruments.size) {
instrumentsIdList.add(Instrument(EnumConfig.getExchangeSegment(response.result.exchangeSegment)!!.toInt(), response.result.instruments[i].toInt()))
marketDataQuotesList.add(QuotesList(EnumConfig.getExchangeSegment(response.result.exchangeSegment)!!.toInt(), response.result.instruments[i].toInt()))
subscriptionList.add(SubscriptionList(EnumConfig.getExchangeSegment(response.result.exchangeSegment)!!.toInt(), response.result.instruments[i].toInt()))
}
val instrumentByIdResult = InstrumentById(instrumentsIdList)
val marketDataQuotes = MarketDataQuotes("ABC", "ABC", marketDataQuotesList, 1111)
val subscriptionList = Subscribe("ABC", "ABC", "Mobile", subscriptionList, 1111)
return#flatMap getDetails(instrumentByIdResult, marketDataQuotes, subscriptionList)
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(object : ErrorCallBack<BaseResponse<List<InstrumentByIdResponse>>>() {
override fun onSuccess(t: BaseResponse<List<InstrumentByIdResponse>>) {
L.d("Success of Search Instrument")
mutableLiveData.value = transform1(t)
}
})
return mutableLiveData
}
private fun transform1(response: BaseResponse<List<InstrumentByIdResponse>>?): List<InstrumentByIdResponse>? {
return response!!.result!!.toList()
}
fun getDetails(instrumentById: InstrumentById, marketDataQuotes: MarketDataQuotes, subscription: Subscribe): Observable<DetailsModel> {
return Observable.zip(
remoteServices.requestInstrumentById(instrumentById),
remoteServices.requestMarketDataQuotes(marketDataQuotes),
remoteServices.requestSubscribe(subscription),
/*Observable.fromArray(remoteServices.requestInstrumentById(instrumentById)),
Observable.fromArray(remoteServices.requestMarketDataQuotes(marketDataQuotes)),
Observable.fromArray(remoteServices.requestSubscribe(subscription)),*/
Function3<List<InstrumentByIdResponse>, MarketDataQuotesResponse, SubscribeResult, DetailsModel>
{ instrumentByIdResponse, marketDataQuotesResponse, subscribeResponse ->
createDetailsModel(instrumentByIdResponse, marketDataQuotesResponse, subscribeResponse)
})
}
private fun createDetailsModel(instrumentByIdResponse: List<InstrumentByIdResponse>, marketDataQuotesResponse: MarketDataQuotesResponse, subscribeResult: SubscribeResult): DetailsModel {
return DetailsModel(instrumentByIdResponse, marketDataQuotesResponse, subscribeResult)
}
data class DetailsModel(
val instrument: List<InstrumentByIdResponse>,
val marketDataQuotes: MarketDataQuotesResponse,
val subscribe: SubscribeResult)
RemoteService :
fun requestInstrumentById(instrumentById: InstrumentById) = ApiService.requestInstrumentById(instrumentById)
APIService :
#Headers("Content-Type: application/json")
#POST("search/instrumentsbyid")
fun requestInstrumentById(#Body instrumentById: InstrumentById): Observable<BaseResponse<List<InstrumentByIdResponse>>>
API Structure
Error that i get on Zip Operator