How to serialize with retrofit and kotlin - kotlin

Iam trying to serialize a response into Session class with Retrofit and Kotlin but i have an error. I am using inheritance here and maybe that is the problem i don't know :
"java.lang.IllegalArgumentException: Unable to create call adapter for retrofit2.Response<com.mobile.myapplication.Session> "
open class BaseUser {
var id:Int=0
var correo:String=""
var nombre:String=""
var apellido:String=""
var direccion:String=""
var telefono:String=""
var imagen:String=""
}
class Permiso {
var id:Int=0
var aplicacionId:Int=0
var aplicacionName:String=""
var lectura:Boolean=false
var escritura:Boolean=false
}
class Session: BaseUser() {
var token:String=""
var permiso:List<Permiso> = emptyList()
}
class LocalViewModel(private val retro:Retro= Retro()):ViewModel(){
private val _result = MutableStateFlow("")
val result:StateFlow<String> = _result
init {
viewModelScope.launch {
val jsonObject= JSONObject()
jsonObject.put("correo", "xxxxxx.com")
jsonObject.put("password", "xxxxx")
Log.d("payload",jsonObject.toString())
retro.token(jsonObject.toString())
}
}
}
#Composable
fun main(vm: LocalViewModel= viewModel()){
Text(text = "Hello world")
}
interface APIService {
#POST("api/Auth/SignIn")
fun requestToken(#Body requestBody: RequestBody): Response<Session>
}
class Retro{
fun getinstance(): APIService? {
var service:APIService?=null
try {
// Create Retrofit
val retrofit = Retrofit.Builder()
.baseUrl("xxxxxxxxx")
.addConverterFactory(GsonConverterFactory.create())
.build()
// Create Service
service = retrofit.create(APIService::class.java)
}catch (err:Error){
Log.e("RETROFIT_ERROR", err.toString())
}
return service
}
fun token(payload:String){
val instance=getinstance()
val requestBody = payload.toRequestBody("application/json".toMediaTypeOrNull())
val response = instance?.requestToken(requestBody)
Log.d("response",response.toString())
}
}
API response is like this :
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJOb21icmUiOiJSaWNoYXJkIiwiQXBlbGxpZG8iOiJWw61xdWV6IiwiQ29ycmVvIjoiUnZpcXVlekBzb2Zub21pYy5jb20iLCJEaXJlY2Npb24iOiJIZXJlZGlhIiwiVGVsZWZvbm8iOiJQw6lyZXoiLCJuYmYiOjE2NTc3MjQzOTksImV4cCI6MTY1Nzc0OTU5OSwiaXNzIjoiaHR0cHM6Ly9zb2Zub21pY2FwaS5henVyZXdlYnNpdGVzLm5ldC8ifQ.HtBEe1XlqyU0YBVyGJ1fs-EUiJn8vbWKqvNci2tOboU",
"id": 26,
"correo": "xxxxx.com",
"nombre": "xx",
"apellido": "xx",
"direccion": "xxx",
"telefono": "xx",
"imagen": null,
"permiso": []
}
What is the problem ???

I don't know why but with Response does not work only with Call instead Response!:
interface APIService {
#POST("api/Auth/SignIn")
fun requestToken(#Body requestBody: RequestBody): Call<Session>
}
fun token(payload:String):Session?{
val instance=getinstance()
val requestBody = payload.toRequestBody("application/json".toMediaTypeOrNull())
var session: Session?=null
val response = instance?.requestToken(requestBody) ?: return null
response.enqueue(object : Callback<Session?> {
override fun onResponse(call: Call<Session?>?, response: Response<Session?>) {
val statusCode = response.code()
if (statusCode!=200){
return
}
session = response.body()
}
override fun onFailure(call: Call<Session?>?, t: Throwable?) {
return
}
})
return session
}

Related

Chain validation failed

I'm having the below Okhttp code:
val client = OkHttpClient()
val mediaType = MediaType.parse("application/x-www-form-urlencoded")
val body = RequestBody.create(mediaType, "tenant_id=xxxx&client_id=xxxx&client_secret=xxxx&resource=xxxx&grant_type=client_credentials")
val request = Request.Builder()
.url("https://sxxx.com/axxx/oauth2/token")
.post(body)
.addHeader("Content-Type", "application/x-www-form-urlencoded")
.build()
val response = client.newCall(request).execute()
And want to convert it usint ktor, so I wrote the below:
class Greeting {
private val httpClient = HttpClient {
}
#Throws(Exception::class)
suspend fun greeting(): String {
val response = httpClient.request {
method = HttpMethod.Post
url {
protocol = URLProtocol.HTTPS
host = "sxxx.com"
path("axxx/oauth2/token")
// encodedParameters
trailingQuery = true
parameters.append("tenant_id", "xxxx")
parameters.append("client_id", "xxxx")
parameters.append("client_secret", "xxxx")
parameters.append("resource", "xxxx")
parameters.append("grant_type", "client_credentials")
}
headers {
append(HttpHeaders.ContentType, "application/x-www-form-urlencoded")
}
}
return response.bodyAsText()
}
}
And calling my new code as:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyApplicationTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
val scope = rememberCoroutineScope()
var text by remember {
mutableStateOf("Loading")
}
LaunchedEffect(true){
scope.launch {
text = try {
Greeting().greeting()
} catch (e: Exception) {
e.localizedMessage ?: "error"
}
}
}
Greeting(text)
}
}
}
}
}
#Composable
fun Greeting(text: String) {
Text(text = text)
}
But instead of getting the required token, I'm getting the response: Chain validation failed
Make sure that:
Your server certificate is valid.
Your android system datetime is correct.

take access token from Shared Preferences and pass in base url next time

I want to know how can I access my token stored in Shared Preferences and pass it in the url when required.
RetrofitClient.kt file :
class OAuthInterceptor(private val tokenType: String, private val access_token: String):
Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
var request = chain.request()
request = request.newBuilder().header("Authorization", "$tokenType $access_token").build()
return chain.proceed(request)
}
}
val client = OkHttpClient.Builder()
.addInterceptor(OAuthInterceptor("Bearer", access_token))
.build()
val retrofit = Retrofit.Builder()
.baseUrl("___________________")
.client(client)
.build()
object RetrofitClient {
private const val BASE_URL = "_______"
private val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val original = chain.request()
val requestBuilder = original.newBuilder()
.method(original.method, original.body)
val request = requestBuilder.build()
chain.proceed(request)
}.build()
val instance: MyApi by lazy{
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
retrofit.create(MyApi::class.java)
}
}
Below is my SharedPrefManager file :
class SharedPrefManager private constructor(private val mCtx: Context) {
val user: User
get() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
return User(
sharedPreferences.getString(user.access_token, ""),
sharedPreferences.getString(user.user_id.toString(), "" ),
sharedPreferences.getString(user.user_name, ""),
sharedPreferences.getString(user.status, ""),
sharedPreferences.getString(user.role_id,""),
sharedPreferences.getString(user.phone, "")
)
}
fun saveUser(user: User) {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.putString("access_token", user.access_token)
editor.putString("id", user.user_id)
editor.putString("username", user.user_name)
editor.putString("status", user.status)
editor.putString("role_id", user.role_id)
editor.putString("phone", user.phone)
editor.apply()
}
fun clear() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
val editor = sharedPreferences.edit()
editor.clear()
editor.apply()
}
companion object {
private const val SHARED_PREF = "my_shared_pref"
private var mInstance: SharedPrefManager? = null
#Synchronized
fun getInstance(mCtx: Context): SharedPrefManager {
if (mInstance == null) {
mInstance = SharedPrefManager(mCtx)
}
return mInstance as SharedPrefManager
}
}
}
You should use the name of the save preference when retrieving them.
Try to change your SharedPrefManager get method like this:
get() {
val sharedPreferences = mCtx.getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE)
return User(
sharedPreferences.getString("access_token", ""),
sharedPreferences.getString("id", "" ),
sharedPreferences.getString("username", ""),
sharedPreferences.getString("status", ""),
sharedPreferences.getString("role_id",""),
sharedPreferences.getString("phone", "")
)
}

Okhttp interceptor not working showing 401(Unauthenticated) response

OKHTTP Interceptor not working It gives me 401 Response(Unauthenticated/UnAuthorized)
Here is my Interceptor Class
InterceptorClass
class NetworkInterceptor(context: Context) : Interceptor {
private val applicationContext = context.applicationContext
private val sharedPreferenceData = SharedPreferenceData(applicationContext)
override fun intercept(chain: Interceptor.Chain): Response {
if (!isConnectionAvailable()) {
throw NoConnectionException("Error! Connecting to the network")
} else {
val requestBuilder = chain.request().newBuilder()
val token = sharedPreferenceData.getString("token", "")
requestBuilder.addHeader("Authorization", "Bearer $token")
Log.e("Token", "intercept: $token")
return chain.proceed(requestBuilder.build())
}
}
#Suppress("DEPRECATION")
fun isConnectionAvailable(): Boolean {
val cm =
applicationContext.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
cm.activeNetworkInfo.also {
return it != null && it.isConnected
}
}
}
Here is the network instance class
object NetworkInstance {
fun getApi(context: Context): DataApi {
val gsonBuilder = GsonBuilder()
gsonBuilder.setLenient()
val gson = gsonBuilder.create()
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val client = OkHttpClient
.Builder()
.addInterceptor(logging)
.addInterceptor(NetworkInterceptor(context))
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.addInterceptor(logging)
.build()
val retrofit = Retrofit
.Builder()
.client(client)
.baseUrl(BuildConfig.BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
return retrofit.create(DataApi::class.java)
}
}
The issue was because of my Stupidity. I have added BASE_URL inside build.gradle which generates a Static Final variable inside BuildCondig.java which can't be altered. While Adding header BASE_URL can't be changed.

How can I add securityToken to this resultactivity?

D/OkHttp: {"code":60202,"message":"Token required"}
This is server error problem.
I want solve code in this resultactivity.
I already setting securityToken here, but how can I add securityToken to request()?
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val securityToken = SaveSharedPreference.getUserInfo(this)
val result = IntentIntegrator.parseActivityResult(requestCode, resultCode, data)
Client.retrofitService.request(result.contents).enqueue(object : Callback<String?> {
override fun onFailure(call: Call<String?>, t: Throwable) {
}
override fun onResponse(
call: Call<String?>?,
response: Response<String?>?
) {
if (response?.isSuccessful == false) {
val er = Gson().fromJson(response.errorBody()?.charStream(), ErrorResponse::class.java)
Log.d(_tag, "${er.code}:${er.message}")
if (er.code == 60201 || er.code== 60202)
{
Toast.makeText(this#Qrcode, "토큰이 유효하지 않습니다.", Toast.LENGTH_SHORT).show()
} else if (securityToken !=null) {
Log.d(_tag, "status: ${response?.code()}. body: ${response?.body()}")
}
}
}
})
}
}```
You can add additional parameter in your request like:
interface RetrofitService {
#GET("/request")
request(#Query("parameter") parameter: String, #Header("Security-Token") securityToken: String): Call<String>
}
Or,
You can build an OkHttpClient for your Retrofit in creating RetrofitService:
RetrofitService :
interface RetrofitService {
#GET("/request")
request(#Query("parameter") parameter): Call<String>
}
Retrofit Builder:
val securityToken = "securityTokenString"
val okHttpClient = OkHttpClient.Builder()
.addInterceptor { chain ->
val request = chain.request()
val newRequestBuilder = request.newBuilder()
.header("Security-Token", securityToken)
chain.proceed(newRequestBuilder.build())
}
val retrofit = Retrofit.Builder()
.addConverterFactory(gsonConverterFactory)
.client(okHttpClient)
.baseUrl(BASE_URL)
.build()
val retrofitService = retrofit.create(RetrofitService::class.java)

Using Retrofit in Kotlin

I am trying to hit api using retrofit in kotlin
This is my DoinBackGround Method
private fun doinBackground() {
Utility.printMessage("in do in background.....")
try {
val hdr = HashMap<String, String>()
hdr.put("x-csrf-token", Utility.getToken(this.context!!))
val apiInterface = ApiCallRetrofit.getClient(this.mCrypt!!)!!.create(ApiInterface::class.java)
if (what.equals(0)) {
val body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), getQuery(para))
print("header...")
call = apiInterface.hitApi(url, hdr, body)
} else if (what.equals(1)) {
val imgPart = ArrayList<MultipartBody.Part>()
if (files != null) {
if (files.size > 0) {
for (i in files.indices) {
imgPart.add(preparePart("image/*", "document_file[" + files.get(i).key + "]", files.get(i).file))
}
}
call = apiInterface.hitApiImage(url, hdr, getMap(para), imgPart)
}
call?.enqueue(object : Callback<StandardReposnse> {
override fun onResponse(call: Call<StandardReposnse>, response: Response<StandardReposnse>) {
try {
Utility.printMessage("messege...." + response.body().message)
val resp = Gson().toJson(response.body())
Utility.printMessage("Response :$resp")
Utility.longLogPrint(response.body().data, "Full response : ")
Utility.printMessage("Error : " + Gson().toJson(response.errorBody()))
onPostExecute(Parseresponce(response.body()))
} catch (e: Exception) {
Parseresponce(null)
e.printStackTrace()
}
}
override fun onFailure(call: Call<StandardReposnse>, t: Throwable) {
t.printStackTrace()
if (progressDialog != null) {
progressDialog?.dismiss()
}
Parseresponce(null)
}
})
}
} catch (e: Exception) {
e.printStackTrace()
}
}
And this is my interface where I am defining all the POST methods
#POST
abstract fun hitApi(#Url api: String, #HeaderMap header: Map<String, Any>, #Body body: RequestBody): Call<StandardReposnse>
#POST
fun hitApiNoHeader(#Url api: String, #Body requestBody: RequestBody): Call<StandardReposnse>
#POST
fun test(#Url api: String, #HeaderMap headerMap: Map<String, String>, #Body requestBody: RequestBody): Call<JSONObject>
#Multipart
#POST
fun hitApiImage(#Url api: String, #HeaderMap headerMap: Map<String, String>, #PartMap bodyMap: Map<String, RequestBody>, #Part images: List<MultipartBody.Part>): Call<StandardReposnse>
Whenever I am trying to hit the Api I am getting the following exception :
java.lang.IllegalArgumentException: Parameter type must not include a type variable or wildcard: java.util.Map<java.lang.String, ?> (parameter #2)
for method ApiInterface.hitApi
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:720)
at retrofit2.ServiceMethod$Builder.methodError(ServiceMethod.java:711)
at retrofit2.ServiceMethod$Builder.parameterError(ServiceMethod.java:729)
at retrofit2.ServiceMethod$Builder.build(ServiceMethod.java:193)
at retrofit2.Retrofit.loadServiceMethod(Retrofit.java:166)
This is the line where the exception occurs in doinbackground method
call = apiInterface.hitApi(url, hdr, body)
I tried #JvmSuppressWildcards before the RequestBody but it did not work, can anyone suggest whats the actual problem over here, plus nothing is printing in the log though I have used print() function should i use LOG.d?
Here i have fully example for it.
This dependancy add in gradle
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
annotationProcessor 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
Here now create ApiClient.kt file
object ApiClient {
val BASE_URL = "http://yourwebsite/services/"
private var retrofit: Retrofit? = null
val client: Retrofit
get() {
if (retrofit == null) {
retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit!!
}
}
Now create APIInterface.kt
#FormUrlEncoded
#POST("users/login")
fun POST_LOGIN(
#Field("imei_number") imei_number: String,
#Field("device_token") device_token: String,
#Field("mobile") mobile: String,
#Field("password") password: String
): Call<LoginResponse>
#GET("general/init-data")
fun GENERAL_MODULE(
#Header("Authorization") auth_key: String
): Call<InitResponse>
#GET("event-gallery/list")
fun GET_Event_GALLERY(
#Header("Authorization") auth_key: String
): Call<EventListResponse>
#GET("event-gallery/photo-list")
fun GET_Event_GALLERY_PHOTO(
#Header("Authorization") auth_key: String,
#Query("id") id: Int
): Call<EventGallerListResponse>
if Any Header for token the use #Header and also When call #GET that time params use #Query and #Post that time #Fields
Now Response file
data class EventListResponse(
#SerializedName("success")
var success: Boolean,
#SerializedName("data")
var data: EventgalleryModel?,
#SerializedName("server_error"),
#SerializedName("eventgallery")
var eventgallery: ArrayList<EventListData>
var server_error: Boolean,
#SerializedName("message")
var message: String
)
Then create Model class of Response
Now time to Activity code
private fun loadData() {
card_progress.visibility = View.VISIBLE
val apiService = ApiClient.client.create(ApiInterface::class.java)
val call =
apiService.GET_FEE_INSTALMENT_LIST(PreferenceManager.getAuthKey(this#FeesInstalmentActivity)!!)
call.enqueue(object : Callback<FeeInstalmentListResponse> {
override fun onResponse(
call: Call<FeeInstalmentListResponse>,
response: Response<FeeInstalmentListResponse>
) {
card_progress.visibility = View.GONE
val data = response.body()!!.data
if (response.code() == 200 && data != null) {
if (response.body()!!.server_error) {
txt_no_data_fee.visibility = View.VISIBLE
txt_no_data_fee.text = response.body()!!.message
} else {
Log.e("data", data.toString())
if (data != null && data.feesinstalment.isEmpty()) {
txt_no_data_fee.visibility = View.VISIBLE
} else {
txt_no_data_fee.visibility = View.GONE
adapter!!.setItem(data.feesinstalment)
}
}
} else if (response.code() == 401) {
PreferenceManager.removePref(this#FeesInstalmentActivity)
startActivity(
Intent(this#FeesInstalmentActivity, LoginActivity::class.java)
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
)
finish()
} else {
Toast.makeText(
this#FeesInstalmentActivity,
R.string.somethingWrong,
Toast.LENGTH_SHORT
).show()
}
}
override fun onFailure(call: Call<FeeInstalmentListResponse>, t: Throwable) {
card_progress.visibility = View.GONE
Log.e("onFailure", t.message)
txt_no_data_fee.visibility = View.VISIBLE
}
})
}
Adapter
class FeeInstalmentAdapter(
private val context: Context,
private var items: ArrayList<FeeInstalmentListData>
) : RecyclerView.Adapter() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.row_fees_instalment_item, parent, false))
}
#SuppressLint("SetTextI18n")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.due_date.text = DateHelper.parseData(items[position].due_date!!, "yyyy-MM-dd", "dd MMM yyyy")
holder.instalment_title.text = items[position].instalment_title
if (items[position].paid_date == null) {
holder.paid_text.visibility = View.GONE
holder.paid_date.text = context.resources.getString(R.string.UnPaid)
holder.paid_date.setTextColor(Color.parseColor("#DC143C"))
} else {
holder.paid_date.text = DateHelper.parseData(items[position].due_date!!, "yyyy-MM-dd", "dd MMM yyyy")
holder.paid_date.setTextColor(Color.parseColor("#58A259"))
}
//holder.paid_date.text = items[position].paid_date
holder.amount.text = "Rs. " + items[position].amount
holder.amount.setTextColor(Color.parseColor("#ED7136"))
}
override fun getItemCount(): Int {
return items.size
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
fun setItem(holidays: ArrayList<FeeInstalmentListData>) {
items = holidays
notifyDataSetChanged()
}
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val due_date = view.due_date
val instalment_title = view.instalment_title
val paid_date = view.paid_date
val amount = view.amount
val paid_text = view.paidText
}
}
You have used:
#POST
abstract fun hitApi(#Url api: String, #HeaderMap header: Map<String, Any>, #Body body: RequestBody): Call<StandardReposnse>
And exception is:
Parameter type must not include a type variable or wildcard: java.util.Map<java.lang.String, ?> (parameter #2)
And your hitApi #2 param use wildcard actually:
#HeaderMap header: Map<String, ?>
Try to specify argument (just change Any to String). Anyway you are not probably going to put Any object than String in your request header.
Call it in the below mentioned way.
val callWeather = NetworkUtils.getApiInterface().getWeatherResponse("03a7949903004a0bb2590633181104", "1.909,45.909", 7)
callWeather.enqueue(object : Callback<APIXUResponse> {
override fun onResponse(call: Call<APIXUResponse>, response: Response<APIXUResponse>) {
var api :APIXUResponse? = response.body()
}
override fun onFailure(call: Call<APIXUResponse>, t: Throwable) {
}
})