Can Retrofit convert #Path parameters from custom class to primitive types - retrofit2

I have a following Retrofit request:
#GET("event/{id}")
suspend fun getEvent(#Path("id") eventId: Long): Response<Event>
However in my application I'm using the following wrapper for the event id:
data class EventId(val value: Long)
Is it possible to pass EventId class as a path parameter and somehow tell Retrofit how to convert the value to Long?
Here is the method signature I want to have:
#GET("event/{id}")
suspend fun getEvent(#Path("id") eventId: EventId): Response<Event>

The solution I've found is to register converter factory in the Retrofit Builder:
class EventIdConverterFactory : Converter.Factory() {
override fun stringConverter(type: Type, annotations: Array<Annotation>, retrofit: Retrofit) =
if (type == EventId::class.java) {
Converter<Any, String> { (it as EventId).value.toString() }
} else {
super.stringConverter(type, annotations, retrofit)
}
}

Related

How to make Servicestack serialize an implicit string overload the way I want it to?

I have a small class which I am using to make sure the strings sent and received by a service remain URL safe without additional encoding (see below).
Ideally I would like to just apply this type to my DTOs and have Servicestack be smart enough to use the implicit operators.
public class MyDto {
Base64UrlString myString;
}
var dto = new MyDto() { myString = "hello i am url safe"; }
On the client this is received as myString: {}
Is there a more elegant way to do this? I had hoped applying a type this way would "just work"
// used only for Base64UrlEncoder
using Microsoft.IdentityModel.Tokens;
namespace MyDto.ServiceModel.Types
{
public class Base64UrlString
{
private readonly string _base64UrlString;
public Base64UrlString(string str)
{
_base64UrlString = Base64UrlEncoder.Encode(str);
}
public static implicit operator string(Base64UrlString base64UrlString) => base64UrlString.ToString();
public static implicit operator Base64UrlString(string str) => new(str);
public override string ToString() => Base64UrlEncoder.Decode(_base64UrlString);
}
}
You'll need to change your class to a struct to make use of the custom struct behavior you're trying to use in your example.
Also ServiceStack.Text serializers only serializes public properties by default so your DTO should use public properties:
public class MyDto {
public Base64UrlString MyString { get; set; }
}
Alternatively you can configure it to serialize public fields with:
JsConfig.Init(new Config {
IncludePublicFields = true
});

How to change retrofit #GET programatically

I have a app where I use youtube api and make a get request using retrofit, now I want to get a list of videos for a specific keyword, but for that I have to use a different get req everytime so how can I change the get request programatically
Code for calling API
private fun getVideosList() {
val videos = RetrofitInstance.youtubeapi.getYoutubeVideos()
videos.enqueue(object : Callback<YoutubeAPIData?> {
override fun onResponse(call: Call<YoutubeAPIData?>, response: Response<YoutubeAPIData?>) {
val videosList = response.body()?.items
if (videosList != null) {
for(video in videosList) {
Log.d("title", video.snippet.title)
}
}
}
override fun onFailure(call: Call<YoutubeAPIData?>, t: Throwable) {
Toast.makeText(applicationContext, "Unable to fetch results!", Toast.LENGTH_SHORT).show()
Log.d("APIError",t.toString())
}
})
}
Retrofit Instance
object RetrofitInstance {
const val BASE_URL = "https://www.googleapis.com/youtube/v3/"
private val retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val youtubeapi: YoutubeListApi by lazy {
retrofit.create(YoutubeListApi::class.java)
}}
Code for API interface
interface YoutubeListApi {
#GET("search?part=snippet&q=eminem&key=*my_key*")
fun getYoutubeVideos(): Call<YoutubeAPIData>}
Now what I want is to change the #GET("search?part=snippet&q=eminem&key=my_key") in the api interface so that if the keyword is eminem it should be search?part=snippet&q=eminem&key=my_key
and if keyword is dog it should be search?part=snippet&q=dogkey=my_key
Why not use #Query from retrofit?
You could redefine your interface to:
interface YoutubeListApi {
#GET("search")
fun getYoutubeVideos(
#Query("part") part: String,
#Query("q") query: String,
#Query("key") key: String,
): Call<YoutubeAPIData>
}
and then you can call it like getYoutubeVideos("snippets", "eminem", "your key") or getYoutubeVideos("snippets", "dog", "your key").
I think you can even hardcode some values in the URL if you want, but honestly I think you can just use kotlin default values:
interface YoutubeListApi {
#GET("search")
fun getYoutubeVideos(
#Query("q") query: String,
#Query("part") part: String = "snippet",
#Query("key") key: String = "your key",
): Call<YoutubeAPIData>
}
and just pass the query getYoutubeVideos("eminem"). I haven't double checked this, but I think it could work.

How to annotate a Kotlin class constructor that might throw an exception?

I would like to keep my Kotlin data class as simple as possible. It is initialized only from a JSONObject like this:
data class Foo(val json: JSONObject)
{
#JvmField val bar: String = json.getString("bar")
}
Is there any way to annotate the class, that the constructor might throw a JSONException?
I think
class Foo #Throws(JSONException::class) constructor(val json: JSONObject) {
// code
}
should work
Yes you can, your class declaration has an implicit primary constructor
You can annotate the constructor after you define that explicitly:
data class Foo #Throws(JSONException::class) constructor(val json: JSONObject)
The same way you do for a function:
import kotlin.jvm.Throws
class Foo #Throws(JSONException::class) constructor(val json: JSONObject) {
init {
// ...
}
}
This becomes the following Java signature:
public Foo(JSONObject json) throws JSONException

RecyclerView(Kotlin): Adding an swipe to delete functionality on a ToDo app with data in SQL database

I am new to kotlin and android studio and currently I am trying to build an todo list applications with my own ideas. Its mostly done but I have to add edit and delete functionality to the tasks that user adds. The tasks that user adds are stored on the device using SQLiteDatabase. This is the base swipe to delete class that I wrote:
abstract class SwipeToDelete(context: Context, dragDir: Int, swipeDir: Int): ItemTouchHelper.SimpleCallback(dragDir, swipeDir) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
TODO("Not yet implemented")
}
}
This is the delete functionality that I have added which works great with a button:
fun deleteToDo(todoId: Long){
val db = writableDatabase
db.delete(TABLE_TODO_ITEM,"$COL_TODO_ID=?", arrayOf(todoId.toString()))
db.delete(TABLE_TODO,"$COL_ID=?", arrayOf(todoId.toString()))
}
This is the recyclerview adapter that I am using:
class ItemAdapter(val context: Context,val dbHandler: DBHandler, val list: MutableList<ToDoItem>) :
RecyclerView.Adapter<ItemAdapter.ViewHolder>(){
override fun onCreateViewHolder(p0: ViewGroup, p1: Int): ViewHolder {
return ViewHolder(LayoutInflater.from(context).inflate(R.layout.rv_child_item,p0,false))
}
override fun onBindViewHolder(holder: ViewHolder, p1: Int) {
holder.itemName.text = list[p1].itemName
holder.itemName.isChecked = list[p1].isCompleted
holder.itemName.setOnClickListener{
list[p1].isCompleted = !list[p1].isCompleted
dbHandler.updateToDoItem(list[p1])
}
}
override fun getItemCount(): Int {
return list.size
}
class ViewHolder(v : View) : RecyclerView.ViewHolder(v){
val itemName : CheckBox = v.findViewById(R.id.cb_item)
}
}
but for some reason I cannot make it work when I try to call the delete function in this Swipetodelete object:
val item = object : SwipeToDelete(this,0,ItemTouchHelper.LEFT){
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
}
}
I also want to add the edit functionality but if i can get this delete functionality to work i can add it.
Since the SwipeToDelete is an abstract class, you can override the onSwiped function on your Fragment/Activity Class.
You can modify your SwipeToDelete class to:
abstract class SwipeToDelete(context: Context, dragDir: Int, swipeDir: Int): ItemTouchHelper.SimpleCallback(dragDir, swipeDir) {
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
}
And then override the onSwiped function inside your fragment/activity and attach it to your recyclerview:
val item = object : SwipeToDelete(this,0,ItemTouchHelper.LEFT){
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
deleteToDo(todoId)
}
}
ItemTouchHelper(item).attachToRecyclerView(recycler)

need help when i try to custom hide password kotlin

I want user write "A" for example and see "b", I want some different way with custom keyboard for the app
this what I tried today but not worked
import android.text.method.PasswordTransformationMethod
import android.view.View
class passit: PasswordTransformationMethod() {
override fun getTransformation(source: CharSequence?, view: View?): CharSequence {
return turnit(source!!) }
class turnit(val s:CharSequence):CharSequence{
override val length: Int= s.length
override fun get(index: Int): Char {
return 'f' }
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
return s.subSequence(0,s.length) }
}
}
mian give the error: transformationMethod of type cannot be invoked as a function
Petitionadd.transformationMethod(passit())
The function on TextView you're trying to call is setTransformationMethod:
textView.setTransformationMethod(Passit())
Which you can also access from Kotlin as a property:
textView.transformationMethod = Passit()

Resources