Here's the code where i am getting exception, tried various ways to implement context but nothing is working out.
class GrowthStoryFragment : Fragment() {
private val TAG = "GrowthStoryFragment"
companion object{
private var countryID = "1"
private var date = "MAT TY"
private var spec = "val"
private var businessUnitID = "2"
private var category = "Fresh Milk"
private var firstReportTypeId = "1" //fixed for growth story and share story
private var isGroup = "false" //fixed to false
}
private val backendApi = WinRetrofitHelper.winApiInstance()
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_growth_story, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
getSpinnerResponse(businessUnitID, isGroup,firstReportTypeId)
getSuperRegionName(countryID, date,spec," ",businessUnitID, category, firstReportTypeId, isGroup)
growth_spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener{
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val item = parent?.getItemAtPosition(position) as RespTwo
category = item.nameValue
Log.e(TAG,"Category name is: " + category)
getSuperRegionName(countryID, date,spec," ",businessUnitID, category, firstReportTypeId, isGroup)
}
}
}
private fun getSpinnerResponse(businessUnitID: String, isGroup: String, firstReportTypeId: String){
val request = backendApi.getCategoryBusinessUnit(businessUnitID, isGroup, firstReportTypeId)
request.enqueue(object : Callback<List<RespTwo>> {
override fun onFailure(call: Call<List<RespTwo>>?, t: Throwable?) {
Log.e(TAG, "Failure Super Region Name: ")
}
override fun onResponse(call: Call<List<RespTwo>>?, response: Response<List<RespTwo>>?) {
val myresponse = response?.body()
if (response?.body() != null && !response?.body()!!.isEmpty()) {
growth_spinner.adapter = GrowthSpinnerAdapter(response?.body())
Log.e(TAG, "Super Region Name: " + myresponse?.get(0)?.nameValue)
} else {
Toast.makeText(context!!.applicationContext, "Data Not Available!", Toast.LENGTH_LONG).show()
}
}
})
}
private fun getSuperRegionName(countryID: String, date: String, spec: String, superMarket: String,businessUnitID: String, category: String, firstReportTypeId: String, isGroup: String) {
val request = backendApi.getSuperRegion(countryID)
request.enqueue(object : Callback<List<RespMy>> {
override fun onFailure(call: Call<List<RespMy>>?, t: Throwable?) {
Log.e(TAG, "Failure Super Region Name: ")
}
override fun onResponse(call: Call<List<RespMy>>?, response: Response<List<RespMy>>?) {
val myresponse = response?.body()
if (response?.body() != null && !response?.body()!!.isEmpty()) {
getDataFromApi(countryID, date, spec, myresponse?.get(0)!!.nameValue, businessUnitID, category, firstReportTypeId, isGroup)
Log.e(TAG, "Super Region Name: " +countryID+" "+ date+" "+ spec+" "+ myresponse?.get(0)?.nameValue+" "+businessUnitID+" "+ category+" "+ firstReportTypeId+" " + isGroup+" ")
} else {
Toast.makeText(myApplicationContext, "Data Not Available!", Toast.LENGTH_LONG).show()
}
}
})
}
}
Please suggest something and i have heard a viewModel conversion of the requests made directly from fragments can fix this out but i don't know how to do that. Please help in either way.
Update:
Don't want to use static context here
ViewModel and Repository pattern is way to go.
You are doing async network call on Main thread, very bad idea.
For learning purposes - here is how to get your code working:
replace:
myApplicationContext = context!!.applicationContext
with:
myApplicationContext = requireContext()
or better - get rid of this variable entirely and just use requireContext() instead.
Toasts are usually applied to the activity rather than an generic context, and demanding the context via context!! should be avoided; it is optional for a reason. I would try the following after defining #string/data_not_available and #string/super_region_name_template in the string resource xml:
val body = response?.body()
if (body?.isNotEmpty() == true) {
growth_spinner.adapter = GrowthSpinnerAdapter(body)
Log.e(TAG, getString(R.string.super_region_name_template, body.get(0).nameValue)) // if applicable, use .first() over .get(0)
} else {
activity?.run {
Toast.makeText(this, getString(R.string.data_not_available), Toast.LENGTH_LONG).show()
}
}
try to use this
myApplicationContext: Context = this.context ?: return
then
Toast.makeText(myApplicationContext, "Data Not Available!",Toast.LENGTH_LONG).show()
for more details, you can see this link
Related
I m getting data from two End points using flows and assigning those two list to temporary list in ViewModel. For this purpose, I'm using combine function and returning result as stateFlows with stateIn operator but that's not working. Can anyone point me out where I go wrong please.
ViewModel.kt
private val _movieItem: MutableStateFlow<State<List<HomeRecyclerViewItems>>> =
MutableStateFlow(State.Loading())
val movieItems: StateFlow<State<List<HomeRecyclerViewItems>>> = _movieItem
fun getHomeItemList() {
viewModelScope.launch {
val testList: Flow<State<List<HomeRecyclerViewItems.Movie>>> =
settingsRepo.getMovieList().map {
State.fromResource(it)
}
val directorList: Flow<State<List<HomeRecyclerViewItems.Directors>>> =
settingsRepo.getDirectorList().map {
State.fromResource(it)
}
_movieItem.value = combine(testList, directorList) { testList, directorList ->
testList + directorList // This is not working as "+" Unresolve Error
}.stateIn(
viewModelScope,
SharingStarted.WhileSubscribed(5000),
State.loading<Nothing>()
) as State<List<HomeRecyclerViewItems>> // Unchecked cast: StateFlow<Any> to State<List<HomeRecyclerViewItems>>
}
Repository.kt
fun getMovieList(): Flow<ResponseAPI<List<HomeRecyclerViewItems.Movie>>> {
return object :
NetworkBoundRepository<List<HomeRecyclerViewItems.Movie>, List<HomeRecyclerViewItems.Movie>>() {
override suspend fun saveRemoteData(response: List<HomeRecyclerViewItems.Movie>) {
}
override fun fetchFromLocal() {
}
override suspend fun fetchFromRemote(): Response<List<HomeRecyclerViewItems.Movie>> =
apiInterface.getMoviesList()
}.asFlow()
}
fun getDirectorList(): Flow<ResponseAPI<List<HomeRecyclerViewItems.Directors>>> {
return object :
NetworkBoundRepository<List<HomeRecyclerViewItems.Directors>, List<HomeRecyclerViewItems.Directors>>() {
override suspend fun saveRemoteData(response: List<HomeRecyclerViewItems.Directors>) {
}
override fun fetchFromLocal() {
}
override suspend fun fetchFromRemote(): Response<List<HomeRecyclerViewItems.Directors>> =
apiInterface.getDirectorsList()
}.asFlow()
}
Network BoundRepository.kt
#ExperimentalCoroutinesApi
abstract class NetworkBoundRepository<RESULT, REQUEST> {
fun asFlow() = flow<ResponseAPI<REQUEST>> {
val apiResponse = fetchFromRemote()
val remotePosts = apiResponse.body()
if (apiResponse.isSuccessful && remotePosts != null) {
emit(ResponseAPI.Success(remotePosts))
} else {
emit(ResponseAPI.Failed(apiResponse.errorBody()!!.string()))
}
}.catch { e ->
e.printStackTrace()
emit(ResponseAPI.Failed("Server Problem! Please try again Later. "))
}
#WorkerThread
protected abstract suspend fun saveRemoteData(response: REQUEST)
#MainThread
protected abstract fun fetchFromLocal()
#MainThread
protected abstract suspend fun fetchFromRemote(): Response<REQUEST>
}
Endpoints with Sealed Class
#GET("directors")
fun getDirectorsList(): Response<List<HomeRecyclerViewItems.Directors>>
#GET("movies")
fun getMoviesList(): Response<List<HomeRecyclerViewItems.Movie>>
sealed class HomeRecyclerViewItems {
class Title(
val id: Int,
val title: String
) : HomeRecyclerViewItems()
class Movie(
val id: Int,
val title: String,
val thumbnail: String,
val releaseDate: String
) : HomeRecyclerViewItems()
class Directors(
val id: Int,
val name: String,
val avator: String,
val movie_count: Int
) : HomeRecyclerViewItems()
}
Fragment.kt
#AndroidEntryPoint
#ExperimentalCoroutinesApi
class SettingsFragment : BaseBottomTabFragment() {
private var _binding: FragmentSettingsBinding? = null
private val binding get() = _binding!!
private val viewModel by viewModels<SettingViewModel>()
#Inject
lateinit var recyclerViewAdapter: RecyclerViewAdapter
#Inject
lateinit var bundle: Bundle
var finalList = mutableListOf<HomeRecyclerViewItems>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
// Inflate the layout for this fragment
_binding = FragmentSettingsBinding.inflate(layoutInflater,container,false)
val view = binding.root
binding.rvMovie.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(activity)
}
bundle.putString("Hello","hihg")
Toast.makeText(activity, "${bundle.getString("Hello")}", Toast.LENGTH_SHORT).show()
finalList.add(HomeRecyclerViewItems.Title(1,"hello"))
return view
}
private fun observeList() {
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED){
launch {
viewModel.movieItems.collect { state ->
when(state){
is State.Loading ->{
}
is State.Success->{
if (state.data.isNotEmpty()){
recyclerViewAdapter = RecyclerViewAdapter()
binding.rvMovie.adapter = recyclerViewAdapter
recyclerViewAdapter.submitList(finalList)
}
}
is State.Error -> {
Toast.makeText(activity, "Error", Toast.LENGTH_SHORT).show()
}
else -> Unit
}
}
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(activity as MainActivity).binding.ivSearch.isGone = true
viewModel.getHomeItemList()
observeList()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
Note: I m following this tutorial simpliedCoding for api data for multirecyclerview but want to implement it with Kotlin State Flow. Any help in this regard is highly appreciated. Thanks.
Your problem is in here
val testList: Flow<State<List<HomeRecyclerViewItems.Movie>>> =
settingsRepo.getMovieList().map {
State.fromResource(it)
}
val directorList: Flow<State<List<HomeRecyclerViewItems.Directors>>> =
settingsRepo.getDirectorList().map {
State.fromResource(it)
}
_movieItem.value = combine(testList, directorList) { testList, directorList ->
testList + directorList
}
They are not returning a List<HomeRecyclerViewItems>, but a State<List<HomeRecyclerViewItems>. Maybe a better name for the variables are testsState and directorsState. After that it will be more clear why you need to unpack the values before combining the lists
_movieItem.value = combine(testsState, directorsState) { testsState, directorsState ->
val homeRecyclerViewItems = mutableListOf<HomeRecyclerViewItems>()
if (testsState is Success) homeRecyclerViewItems.add(testsState.data)
if (directorsState is Success) homeRecyclerViewItems.add(directorsState.data)
homeRecyclerViewItems
}
I try to recover the data of a player with the league of legends API however the response to my request is always null and those without an error message in my logcat.
here is my retrofit call:
public interface LolApiService {
#GET("summoners/by-name/")
Call<SummonerData> getSummonerData (#Query("summonerName")String summonerName, #Query("key") String key);
}
here is my repository:
class LolApiRepository(val application: Application) {
val response = MutableLiveData<SummonerData>()
fun getSummonerID(summonerName: String, key: String): MutableLiveData<SummonerData> {
// val responseData = MutableLiveData<SummonerData>()
val retrofit = Retrofit.Builder()
.baseUrl("https://euw1.api.riotgames.com/lol/summoner/v4/")
.addConverterFactory(GsonConverterFactory.create())
.build()
val service = retrofit.create(LolApiService::class.java)
service.getSummonerData(summonerName, key).enqueue(object : Callback<SummonerData> {
override fun onFailure(call: Call<SummonerData>, t: Throwable) {
Toast.makeText(application, "Error wile accessing the API", Toast.LENGTH_SHORT)
.show()
}
override fun onResponse(call: Call<SummonerData>, resp: Response<SummonerData>) {
Log.d("LolApiRepository", "LolApiRepository:" + resp.body().toString())
if (resp.body() != null) {
Toast.makeText(application, "Success accessing the API", Toast.LENGTH_SHORT)
.show()
response.value = (resp.body() as SummonerData)
} else {
Log.d("LolApiRepository", "LolApiRepository:" + resp.errorBody().toString())
Toast.makeText(application, "Error wile accessing the API", Toast.LENGTH_SHORT)
.show()
}
}
})
return response
}
}
my data model in which I retrieve the result of my query:
class SummonerData {
#SerializedName("id")
#Expose
var id: String? = null
#SerializedName("accountId")
#Expose
var accountId: String? = null
#SerializedName("puuid")
#Expose
var puuid: String? = null
#SerializedName("name")
#Expose
var name: String? = null
#SerializedName("profileIconId")
#Expose
var profileIconId: Int? = null
#SerializedName("revisionDate")
#Expose
var revisionDate: Int? = null
#SerializedName("summonerLevel")
#Expose
var summonerLevel: Int? = null
}
the fragment in which I want to display the data:
class LolStatFragment : Fragment() {
private lateinit var mViewModel: LolApiViewModel
private val apiKey = "api_key=RGAPI-bb27988b-cbb1-4767-b18b-icar8e90c308"
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_lol_stat, container, false)
mViewModel = ViewModelProviders.of(this).get(LolApiViewModel::class.java)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
summoner_search.setOnClickListener {
val summonerName = summoner_name.text.toString()
mViewModel.summonerIds(summonerName,apiKey).observe(viewLifecycleOwner,Observer<SummonerData>{
summoner_ID.text = it.id
Log.d("LolStatFragment", "LolStatFragment:" + it.id)
Toast.makeText(context, "zzzzzzzzz ${it.id}", Toast.LENGTH_SHORT).show()
})
}
}
}
here is the result of my retrofit request on a web browser:
{"id":"OR5-q4c9Mw3jKXcPZw2lXul0tT7eLf4dYNadYrGhQ9mG8-w","accountId":"gOb2ZjN51iRLnRmDJuR5GmfILqP3x-T3qfbKWaTZ9k3dYw","puuid":"9TgzR6qdI_X9Z6xFzV0nFndITN0LSGKKeJ5fol2Ii1a01l4duKvFwpYGJQvBeYkBLkvJc96Sr7DZMg","name":"Practice","profileIconId":4353,"revisionDate":1619525378251,"summonerLevel":209}
thank you to all those who will take the time to answer me !
PS:this is my first question on the forum, I hope to have been clear and to have asked my question correctly,If there's any detail that I left out for this question, feel free to ask.
I finally found the answer to my problem, the url of the network call was not formatted well. here is the code used to retrieve my call url api in my OnResponse method and compare it to that of the browser:
Log.d("LolApiRepository", "LolApiRepository:" + resp.toString())
this is what I had to change in my LolApiService interface :
public interface LolApiService {
#GET("summoners/by-name/{summonerName}?")
Call<SummonerData> getSummonerData (#Path("summonerName") String summonerName, #Query("api_key") String key);
}
I've recently work with Kotlin, and got really stuck with this one problem. I'm trying to return float value receive onResponse of a coroutine api call function. I'm trying to create a class that handle api call and use it on a fragment.
FunctionA.kt
class FunctionA(val context: Context?, val A: Float?, val B: String?){
private var cardApi: CardApi = ApiClient.createApi().create(CardApi::class.java)
....
func getBalance(cardNo: String): Float?{
val cardBalance: Float = null
GlobalScope.launch(Dispatchers.Main) {
val cardDetails = cardApi.getCardBalance(cardNo)
cardDetails.enqueue(object : Callback<Card> {
override fun onFailure(call: Call<Card>, t: Throwable) {
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to t.message!!)
}
override fun onResponse(call: Call<Card>, response: Response<Card>) {
if (response.isSuccessful) {
val card = response.body()!!
cardBalance = card.cardAvailableBalance
} else {
val error: ApiError = ErrorUtils.parseError(response)
val message = error.code + error.message
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to message)
context!!.toast("Errror: " + message)
promptErrorDialog(error)
}
}
})
}}
return cardBalance
}
....
....
}
FragmentClass.kt
class FragmentClass : BaseFragment(){
val galA = 10.5f
val galB = "Test"
private var pass = FunctionA(context!!, valA ,valB)
....
val point = "sasd12125"
private fun gooToo(){
val B = pass.getBalance(point)
print("TEST")
println("value B: " + B)
}
....
}
What happend right now, since the coroutine will take some time in background, val B are null and didn't get the value obtained onResponse. Only after I try to call that functionA again, then the value are updated. I'm not sure if I'm doing it right and I've tried to search for solutions, but it doesn't suit with my current situation. Probably my searching skill are soo bad.
Output
TEST
value B: null
How should I wait for the coroutine to finish before return the cardBalance value?
Proper way to return a single value from a coroutine is to use await().
Now, since you use coroutine to wrap some callback API, that wouldn't work so well. So I would suggest to go with something like this:
val scope = CoroutineScope(Dispatchers.IO)
suspend fun getBalance(cardNo: String): Float{
val res = CompletableDeferred<Float>()
scope.launch {
val cardDetails = cardApi.getCardBalance(cardNo)
cardDetails.enqueue(object : Callback<Card> {
override fun onFailure(call: Call<Card>, t: Throwable) {
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to t.message!!)
}
override fun onResponse(call: Call<Card>, response: Response<Card>) {
if (response.isSuccessful) {
val card = response.body()!!
res.complete(card.cardAvailableBalance)
} else {
val error: ApiError = ErrorUtils.parseError(response)
val message = error.code + error.message
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to message)
res.completeExceptionally(message)
withContext(Dispatchers.Main) {
promptErrorDialog(error)
}
}
}
})
}
return res.await()
}
A few points to consider. First, I used Dispatchers.IO instead of Dispatchers.Main, and switch to Main thread only when needed using withContext(Dispatchers.Main). Otherwise, you're just running your IO on the main thread, coroutine or not.
Second, using GlobalScope is a bad practice, and you should avoid it at all cost. Instead I create a custom scope that you can .cancel() to prevent coroutine leak.
Third, the most correct way would be to return Deferred<Float>, and not Float, since await() is blocking. But I left it for simplicity.
To solve my little problem I end up using a callback to pass the response data. I found that this method work perfectly and easier to understand for my level of understanding. This method skeleton can also be reused for any api service call that I want to use in the future.
FunctionA.kt
class FunctionA(val context: Context?, val A: Float?, val B: String?){
private var cardApi: CardApi = ApiClient.createApi().create(CardApi::class.java)
private var card: Card? = null
interface CardBalanceCallback {
fun processFinish(output: Boolean, cardBalance: Float?)
}
fun getCardBalance(cardNo: String, callback: CardBalanceCallback) = runBlocking {
getBalance(cardNo, callback)
}
private fun getBalance(cardNo: String, callback: CardBalanceCallback) = CoroutineScope(Dispatchers.Main).launch {
try {
val response = cardApi.getCardBalance(cardNo).await()
if (response.isSuccessful) {
card = response.body()
callback.processFinish(true, card!!.cardAvailableBalance)
} else {
callback.processFinish(false, null)
val error: ApiError = ErrorUtils.parseError(response)
val message = when {
error.error.code.isNotEmpty() -> error.error.code + error.error.message
else -> error.code + error.message
}
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to message)
promptErrorDialog(error)
}
} catch (e: HttpException) {
callback.processFinish(false, null)
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to e.message!!)
context!!.toast(e.message.toString())
} catch (e: Throwable) {
callback.processFinish(false, null)
trackEvent(API_READ_CARD_BALANCE_ERROR, ERROR to e.message!!)
context!!.toast( e.message.toString())
}
}
....
....
}
FragmentClass.kt
class FragmentClass : BaseFragment(){
private var funcService = FunctionA(null, null ,null)
....
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(activity!!.application as App).component.inject(this)
val valA = 10.5f
val valB = "Test"
val cardNo = "4001526976443264"
val cardExpDate = "1119"
funcService = FunctionA(context!!, valA ,valB)
getCardBalanceApi(cardNo, cardExpDate)
}
....
private fun getCardBalanceApi(cardNo: String, cardExpDate: String?) {
showLoadingDialog()
funcService.getCardBalance(cardNo, object : SmartPayService.CardBalanceCallback {
override fun processFinish(output: Boolean, cardBalance: Float?) {
dismissLoadingDialog()
if (cardBalance != null) {
checkBalance(cardNo, cardBalance, cardExpDate)
}
}
})
}
....
}
This is some simple changes that I made for this particular problem on my first post. This approach might not be as good or smooth enough as I'm still learning. Hope it help some of you guys. cheers
Make getBalance() a suspend function and then call using lifecycleScope in your fragment
private fun gooToo(){
lifecycleScope.launch {
val B = pass.getBalance(point)
print("TEST")
println("value B: " + B)
}
}
getBalance() function signature would be something like
suspend fun getBalance(): Float = withContext(Dispatchers.IO)
How can I pass the variable day by pressing a button to the apiInterface class?
The fragment FragJornadas, from where I want to pass the variable to #Get in Interface?
class FragJornadas : Fragment() {
var jornada = "1"
var dataList = ArrayList<TodasModel>()
lateinit var recyclerView: RecyclerView
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val view = inflater.inflate(R.layout.jornadas_list, container, false)
val miTexto: TextView = view.findViewById(R.id.tv_Jornadas)
miTexto.text = (getString(R.string.num_jornada))
val numJor = intArrayOf(R.id.tv_01, R.id.tv_02, R.id.tv_03, R.id.tv_04, R.id.tv_05,
R.id.tv_06, R.id.tv_07, R.id.tv_08, R.id.tv_09, R.id.tv_10, R.id.tv_11, R.id.tv_12,
R.id.tv_13, R.id.tv_14, R.id.tv_15, R.id.tv_16, R.id.tv_17, R.id.tv_18, R.id.tv_19,
R.id.tv_20, R.id.tv_21, R.id.tv_22, R.id.tv_23, R.id.tv_24, R.id.tv_25, R.id.tv_26,
R.id.tv_27, R.id.tv_28, R.id.tv_29, R.id.tv_30)
val button = arrayOfNulls<Button>(numJor.size)
for(i in numJor.indices){
button[i] = view.findViewById(numJor[i]) as Button
val buttonValue = i+1
val buttonText = Integer.toString(buttonValue)
button[i]!!.setOnClickListener {
miTexto.text = getString(R.string.num_jornada) + " " + buttonText
jornada = buttonText
getData(jornada)
}
}
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val progressBar: ProgressBar = this.progressBar1
recyclerView = view.findViewById(R.id.recycler_view)
recyclerView.adapter= TodasAdapter(dataList,activity!!)
recyclerView.layoutManager=LinearLayoutManager(activity!!,LinearLayoutManager.VERTICAL,false)
Thread(Runnable {
activity!!.runOnUiThread {
progressBar.visibility = View.VISIBLE
}
try {
var i = 0
while(i < Int.MAX_VALUE){
i++
}
} catch (e: InterruptedException) {
e.printStackTrace()
}
activity!!.runOnUiThread {
progressBar.visibility = View.GONE
}
}).start()
getData(jornada)
}
private fun getData(jornada: String) {
val call: Call<List<TodasModel>> = ApiFederacion.getClient.getTodasJuvenil(jornada)
call.enqueue(object : Callback<List<TodasModel>> {
override fun onResponse(call: Call<List<TodasModel>>?, response: Response<List<TodasModel>>?) {
dataList.addAll(response!!.body()!!)
recyclerView.adapter!!.notifyDataSetChanged()
}
override fun onFailure(call: Call<List<TodasModel>>?, t: Throwable?) {
//progerssProgressDialog.dismiss()
}
})
}
TodasAdapter
class TodasAdapter(private var actList: List<TodasModel>, private val context: Context) : RecyclerView.Adapter<TodasAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.actual_row, parent, false))
}
override fun getItemCount(): Int {
return actList.size
}
#SuppressLint("SetTextI18n", "SimpleDateFormat")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val dataModel= actList.get(position)
holder.itemView.setOnClickListener(){
Toast.makeText(context, dataModel.nomLocal, Toast.LENGTH_LONG).show()
}
val parser = SimpleDateFormat("dd/MM/yyyy") //Formato de fecha obtenida
val formatter = SimpleDateFormat("EE dd 'de' MMMM 'del 'yyyy \n" + "' a las '") //Formato a mostrar
val output = formatter.format(parser.parse(dataModel.fecha + "/2019"))
holder.tv_Fecha.text = output + dataModel.hora
//holder.tv_Fecha.text = dataModel.fecha + "\n" + dataModel.hora
holder.tv_NomLocal.text = dataModel.nomLocal
holder.tv_ResultadoL.text = dataModel.resulLocal
holder.tv_Estado.text = " - " + dataModel.estadoPartido + " - "
holder.tv_ResultadoV.text = dataModel.resulVisitante
holder.tv_NomVisitante.text = dataModel.nomVisitante
Picasso.get()
.load("https://ffcv.es/ncompeticiones/" + dataModel.escudoLocal)
.fit()
//.resize(150, 150)
.into(holder.imageEscLocal)
Picasso.get()
.load("https://ffcv.es/ncompeticiones/" + dataModel.escudoVisitante)
.fit()
.into(holder.imageEscVisi)
}
class ViewHolder(itemLayoutView: View) : RecyclerView.ViewHolder(itemLayoutView) {
var tv_Fecha:TextView
var tv_NomLocal:TextView
var tv_ResultadoL:TextView
var tv_Estado:TextView
var tv_ResultadoV:TextView
var tv_NomVisitante:TextView
var imageEscLocal: ImageView
var imageEscVisi: ImageView
init {
tv_Fecha = itemLayoutView.findViewById(R.id.tv_Fecha)
tv_NomLocal = itemLayoutView.findViewById(R.id.tv_Equipo_Local)
tv_ResultadoL = itemLayoutView.findViewById(R.id.tv_Result_Local)
tv_Estado = itemLayoutView.findViewById(R.id.tv_Estado)
tv_ResultadoV = itemLayoutView.findViewById(R.id.tv_Result_Visitante)
tv_NomVisitante = itemLayoutView.findViewById(R.id.tv_Equipo_Visitante)
imageEscLocal = itemLayoutView.findViewById(R.id.iv_Local) as ImageView
imageEscVisi = itemLayoutView.findViewById(R.id.iv_Visi) as ImageView
}
}
}
And the ApiInterface class to receive it. Being the url I want to get is:
#GET("server.php?action=getResultados&cmp=328{jor}tmp=2018/2019") fun
getTodasJuvenil(
#Path("jor") jornada: String ): Call<List<TodasModel>>
The url I want to get is:
"server.php?action=getResultados&cmp=328&jor=1&tmp=2018/2019"
this is the error:
java.lang.IllegalArgumentException: URL query string
"action=getResultados&cmp=328{jor}&tmp=2018/2019" must not have
replace block. For dynamic query parameters
at com.myapplication.Jornadas.FragJornadas.getData(FragJornadas.kt:91)
at
com.myapplication.Jornadas.FragJornadas.onViewCreated(FragJornadas.kt:87)
Using #Path you're telling retrofit to replace that variable in the path of the url, but you want in the query string. The path is the bit after the domain and before the query string - in your case would be server.php and maybe some bits that come before, which I can't say without seeing the full url.
To add a parameter to the query string you want to use #Query and you don't want to specify it in the query string in the #GET annotation. So you could change things to:
#GET("server.php?action=getResultados&cmp=328&tmp=2018/2019") fun
getTodasJuvenil(
#Query("jor") jornada: String ): Call<List<TodasModel>>)
You've removed {jor} from the query string and replaced #Path with #Query.
Now, retrofit should add a query parameter with the desired jornada.
#Fred thanks for your answers, my mistake was not the previous one, I was not deleting the list, it was added at the end, the solution is:
dataList.clear()
override fun onResponse(call: Call<List<TodasModel>>?, response: Response<List<TodasModel>>?) {
dataList.clear()
dataList.addAll(response!!.body()!!)
recyclerView.adapter!!.notifyDataSetChanged()
}
Trying to pass the device ID to the interface and I am not getting anywhere. I essentially want to store the device ID to a variable and put it in the #Get for the interface.
I tried using #Path but I am not familiar enough with it to use it.
The url needs to look like this on the call
http://xxx.xx.xxx.xxx/apps/api/109/devices/65?access_token=xxxxxx
Interface
interface DeviceDetailsAPIClient {
#GET("devices/<item id here>")
fun getDevicesDetailsAsync(#Query("access_token") access_token: String): Deferred<Response<DeviceDetails>>
}
MainActivity2 where the ID is passed to
class MainActivity2 : AppCompatActivity() {
private val tag : String = MainActivity2::class.java.simpleName
var deviceID: String = intent.getStringExtra("deviceID")
private lateinit var adapterDetails: DeviceDetailsAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
rv_devices.layoutManager = LinearLayoutManager(this)
rv_devices.hasFixedSize()
adapterDetails = DeviceDetailsAdapter(listOf()) { deviceDetails: DeviceDetails -> deviceDetails }
rv_devices.adapter = adapterDetails
loadDeviceDetails()
}
private fun loadDeviceDetails() {
GlobalScope.launch(Dispatchers.Main) {
try {
val webResponseDetails = deviceDetailsApi.getDevicesDetailsAsync(access_token = "xxxxxx").await()
if (webResponseDetails.isSuccessful) {
val deviceDetails: DeviceDetails? = webResponseDetails.body()
Log.d(tag, deviceDetails?.toString())
//adapterDetails.deviceDetails = deviceDetails ?: listOf()
adapterDetails.notifyDataSetChanged()
} else {
Log.e(tag, "Error ${webResponseDetails.code()}")
Toast.makeText(this#MainActivity2, "Error ${webResponseDetails.code()}", Toast.LENGTH_LONG).show()
}
} catch (e: IOException) {
Log.e(tag, "Exception " + e.printStackTrace())
Toast.makeText(this#MainActivity2, "Exception ${e.message}", Toast.LENGTH_LONG).show()
}
}
}
}
This is the var I am storing the ID in
var deviceID: String = intent.getStringExtra("deviceID")
Now how do I get that to the interface?
The #Path annotation can be used to change the URL:
interface DeviceDetailsAPIClient {
#GET("devices/{deviceId}")
fun getDevicesDetailsAsync(
#Path("deviceId") deviceId: Long,
#Query("access_token") access_token: String
): Deferred<Response<DeviceDetails>>
}