How to fix wrong constructor parameters in kotlin - kotlin

I have an interesting error with reflections in kotlin.
So, im using 'argTypes' method for getting all parameter type of args.
private fun argTypes(vararg args: Any): Array<Class<*>> {
val argTypes = ArrayList<Class<*>>()
args.forEach { argTypes.add(it::class.java) }
return argTypes.toTypedArray()
}
Im using it with like that:
fun <T> newInstance(clazz: Class<*>, argTypes: Array<Class<*>>, vararg args: Any): T {
return clazz.getDeclaredConstructor(*argTypes).newInstance(*args) as T
}
In the end:
ReflectionUtil.instance.newInstance<IBossBar>(
PacketBossBar1_13_R2::class.java,
TextComponent("asd"),Color.BLUE,Style.PROGRESS,100F)
I use a float parameters thats '100F'.
When i use that method, the type is going to be java.lang.Float but my 'PacketBossBar1_13_R2' constructor has a float parameters like that:
constructor(
message: TextComponent,
color: Color,
style: Style,
progress: Float
): this(ComponentSerializer.toString(message), color, style, progress)
When i getting the constructor as a manual, its return
public io.github.utsukushihito.utsutil.nms.v1_13_R2.PacketBossBar1_13_R2(net.md_5.bungee.api.chat.TextComponent,io.github.utsukushihito.utsutil.api.bossbar.enums.Color,io.github.utsukushihito.utsutil.api.bossbar.enums.Style,float)
When i use automatic way its returning NoSucMethodException like that:
java.lang.NoSuchMethodException: io.github.utsukushihito.utsutil.nms.v1_13_R2.PacketBossBar1_13_R2.<init>(net.md_5.bungee.api.chat.TextComponent, io.github.utsukushihito.utsutil.api.bossbar.enums.Color, io.github.utsukushihito.utsutil.api.bossbar.enums.Style, java.lang.Float)
Also my ReflectionUtil and PackateBossBar classes:
package io.github.utsukushihito.utsutil.api.misc
import org.bukkit.Bukkit
import java.lang.reflect.Field
import java.lang.reflect.Method
import java.lang.reflect.Modifier
import java.util.*
import java.util.stream.Collectors
class ReflectionUtil {
private val nmsVersion: String
get() = nms().split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[3]
val craftBukkitVersion: String
get() = cb().split("\\.".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[3]
private fun nms(): String {
return exec<Any>(Bukkit.getServer(), "getServer").javaClass.getPackage().name
}
private fun cb(): String {
return Bukkit.getServer().javaClass.getPackage().name
}
fun getPackageName(nmsObject: Any): String {
return nmsObject.javaClass.getPackage().name
}
fun getBukkitClass(craftObject: Any): Class<*> {
var clazz: Class<*> = craftObject.javaClass
while (clazz.canonicalName.contains(".craftbukkit.")) {
clazz = clazz.superclass
}
return clazz
}
fun getCustomBukkitClass(className: String): Class<*> {
return Class.forName("org.bukkit.craftbukkit.$nmsVersion.$className")
}
fun getNMSClass(name: String): Class<*> {
return Class.forName("net.minecraft.server.$nmsVersion.$name")
}
fun <T> execStatic(clazz: Class<*>, methodName: String, vararg args: Any): T {
val method = getMethod(clazz, methodName, *argTypes(*args))
val wasAccessible = method.isAccessible
method.isAccessible = true
try {
return method.invoke(null, *args) as T
} finally {
method.isAccessible = wasAccessible
}
}
fun <T> execStatic(clazz: Class<*>, methodName: String, argTypes: Array<Class<*>>, vararg args: Any): T {
val method = getMethod(clazz, methodName, *argTypes)
val wasAccessible = method.isAccessible
method.isAccessible = true
try {
return method.invoke(null, *args) as T
} finally {
method.isAccessible = wasAccessible
}
}
fun <T> exec(obj: Any, methodName: String, argTypes: Array<Class<*>>, vararg args: Any): T {
val aClass = obj.javaClass
val method = getMethod(aClass, methodName, *argTypes)
val wasAccessible = method.isAccessible
method.isAccessible = true
try {
return method.invoke(obj, *args) as T
} finally {
method.isAccessible = wasAccessible
}
}
fun getMethod(aClass: Class<*>, methodName: String, vararg argTypes: Class<*>): Method {
return aClass.getDeclaredMethod(methodName, *argTypes)
}
fun findMethod(aClass: Class<*>, returnType: Class<*>, vararg argTypes: Class<*>): Method {
return findMethods(aClass, returnType, *argTypes)[0]
}
fun findMethods(aClass: Class<*>, returnType: Class<*>, vararg argTypes: Class<*>): List<Method> {
val methods = ArrayList<Method>()
for (m in aClass.declaredMethods) {
if (m.returnType == returnType && m.parameterTypes.size == argTypes.size) {
val mLookup = aClass.getMethod(m.name, *argTypes)
if (mLookup != null) methods.add(mLookup)
}
}
return methods
}
fun <T> exec(obj: Any, methodName: String, vararg args: Any): T {
return exec(obj, methodName, argTypes(*args), *args) as T
}
fun <T> getField(clazz: Class<*>, fieldName: String): T {
val field = getFieldFromClass(clazz, fieldName)
val wasAccessible = field.isAccessible
field.isAccessible = true
try {
return field.get(null) as T
} finally {
field.isAccessible = wasAccessible
}
}
fun <T> getField(obj: Any, fieldName: String): T {
val field = getFieldInternal(obj, fieldName)
val wasAccessible = field.isAccessible
field.isAccessible = true
try {
return field.get(obj) as T
} finally {
field.isAccessible = wasAccessible
}
}
fun getFieldInternal(obj: Any, fieldName: String): Field {
return getFieldFromClass(obj.javaClass, fieldName)
}
fun getFieldFromClass(aClass: Class<*>, fieldName: String): Field {
return try {
aClass.getDeclaredField(fieldName)
} catch (e: NoSuchFieldException) {
try {
aClass.getField(fieldName)
} catch (e1: NoSuchFieldException) {
getFieldFromClass(aClass.superclass, fieldName)
}
}
}
fun setField(obj: Any, fieldName: String, field: Any?) {
val declaredField = getFieldInternal(obj, fieldName)
val wasAccessible = declaredField.isAccessible
declaredField.isAccessible = true
try {
declaredField.set(obj, field)
} finally {
declaredField.isAccessible = wasAccessible
}
}
fun <T> newInstance(clazz: Class<*>, argTypes: Array<Class<*>>, vararg args: Any): T {
return clazz.getDeclaredConstructor(*argTypes).newInstance(*args) as T
}
fun <T> newInstance(clazz: Class<*>, vararg args: Any): T {
return newInstance(clazz, argTypes(*args), *args)
}
fun <T> newInstance(className: String, vararg args: Any): T {
return newInstance(className, argTypes(*args), *args)
}
private fun argTypes(vararg args: Any): Array<Class<*>> {
val argTypes = ArrayList<Class<*>>()
args.forEach { argTypes.add(it::class.java) }
return argTypes.toTypedArray()
}
fun dumpMethods(aClass: Class<*>): List<String> {
val methods = aClass.declaredMethods
val methodDescriptions = ArrayList<String>()
val version = nmsVersion
for (m in methods) {
var parmString = Arrays.toString(Arrays.stream(m.parameterTypes).map<String>{ it.name }.toArray())
parmString = parmString.substring(1, parmString.length - 1)
var description = ((if (Modifier.isPublic(m.modifiers)) "public " else if (Modifier.isPrivate(m.modifiers)) "private " else "")
+ (if (Modifier.isStatic(m.modifiers)) "static " else "")
+ m.returnType + " " + m.name
+ "(" + parmString + ")")
description = description
.replace("class net.minecraft.server.$version.".toRegex(), "")
.replace("net.minecraft.server.$version.".toRegex(), "")
.replace("java.lang.".toRegex(), "")
methodDescriptions.add(description)
}
val list = ArrayList<String>()
list.add(aClass.toString().replace("class net.minecraft.server.$version.".toRegex(), "")
.replace("net.minecraft.server.$version.".toRegex(), "")
.replace("java.lang.".toRegex(), "") + ":")
list.addAll(methodDescriptions.stream().sorted { obj, anotherString -> obj.compareTo(anotherString) }.collect(Collectors.toList()))
return list
}
companion object {
val instance = ReflectionUtil()
}
}
package io.github.utsukushihito.utsutil.nms.v1_13_R2
import io.github.utsukushihito.utsutil.api.bossbar.addBossBarForPlayer
import io.github.utsukushihito.utsutil.api.bossbar.enums.Color
import io.github.utsukushihito.utsutil.api.bossbar.enums.Property
import io.github.utsukushihito.utsutil.api.bossbar.enums.Style
import io.github.utsukushihito.utsutil.api.bossbar.removeBossBarForPlayer
import io.github.utsukushihito.utsutil.api.misc.ReflectionUtil
import io.github.utsukushihito.utsutil.api.nms.IBossBar
import net.md_5.bungee.api.chat.TextComponent
import net.md_5.bungee.chat.ComponentSerializer
import net.minecraft.server.v1_13_R2.BossBattle
import net.minecraft.server.v1_13_R2.IChatBaseComponent
import net.minecraft.server.v1_13_R2.PacketPlayOutBoss
import org.bukkit.Location
import org.bukkit.craftbukkit.v1_13_R2.entity.CraftPlayer
import org.bukkit.entity.Player
import java.util.*
class PacketBossBar1_13_R2(
private var message: String,
private var color: Color,
private var style: Style,
private var progress: Float,
vararg properties: Property
) : IBossBar{
private val receivers = ArrayList<Player>()
private val uuid = UUID.randomUUID()
private var darkenSky: Boolean = false
private var playMusic: Boolean = false
private var createFog: Boolean = false
private var visible: Boolean = false
constructor(
message: TextComponent,
color: Color,
style: Style,
progress: Float,
vararg properties: Property
): this(ComponentSerializer.toString(message), color, style, progress, *properties)
// I Try to run this cotr with the reflection util.
constructor(
message: TextComponent,
color: Color,
style: Style,
progress: Float
): this(ComponentSerializer.toString(message), color, style, progress)
init {
setMessage(message)
setProgress(progress)
properties.forEach {
setProperty(it,true)
}
}
override fun getPlayers(): Collection<Player> {
return ArrayList(this.receivers)
}
override fun addPlayer(player: Player) {
if (!receivers.contains(player)) {
receivers.add(player)
sendPacket(PacketPlayOutBoss.Action.ADD, player)
player.addBossBarForPlayer(this)
}
}
override fun removePlayer(player: Player) {
if (receivers.contains(player)) {
receivers.remove(player)
sendPacket(PacketPlayOutBoss.Action.REMOVE, player)
player.removeBossBarForPlayer(this)
}
}
override fun getColor(): Color {
return color
}
override fun setColor(color: Color) {
if (color != this.color) {
this.color = color;
sendPacket(PacketPlayOutBoss.Action.UPDATE_STYLE, null);
}
}
override fun getStyle(): Style {
return style
}
override fun setStyle(style: Style) {
if (style != this.style) {
this.style = style
sendPacket(PacketPlayOutBoss.Action.UPDATE_STYLE, null)
}
}
override fun setProperty(property: Property, flag: Boolean) {
when (property) {
Property.DARKEN_SKY -> darkenSky = flag
Property.PLAY_MUSIC -> playMusic = flag
Property.CREATE_FOG -> createFog = flag
}
sendPacket(PacketPlayOutBoss.Action.UPDATE_PROPERTIES, null)
}
override fun setMessage(message: String) {
if (!message.startsWith("{") || !message.endsWith("}")) {
throw IllegalArgumentException("Invalid JSON")
}
if (message != this.message) {
this.message = message
sendPacket(PacketPlayOutBoss.Action.UPDATE_NAME, null)
}
}
override fun getMessage(): String {
return message;
}
override fun setVisible(flag: Boolean) {
if (flag != this.visible) {
this.visible = flag
sendPacket(if (flag) PacketPlayOutBoss.Action.ADD else PacketPlayOutBoss.Action.REMOVE, null)
}
}
override fun isVisible(): Boolean {
return visible
}
override fun getProgress(): Float {
return progress
}
override fun setProgress(progress: Float) {
if (progress > 1) this.progress = progress / 100F
if (progress != this.progress) {
this.progress = progress
sendPacket(PacketPlayOutBoss.Action.UPDATE_PCT, null)
}
}
private fun sendPacket(action: PacketPlayOutBoss.Action, player: Player?) {
try {
val packet = PacketPlayOutBoss()
val ref = ReflectionUtil.instance
ref.setField(packet,"a",uuid)
ref.setField(packet,"b",action)
ref.setField(packet,"c",IChatBaseComponent.ChatSerializer.a(message)?: "")
ref.setField(packet,"d",progress)
ref.setField(packet,"e", BossBattle.BarColor.a(color.id))
ref.setField(packet,"f",BossBattle.BarStyle.a(style.id))
ref.setField(packet,"g",darkenSky)
ref.setField(packet,"h",playMusic)
ref.setField(packet,"i",createFog)
if (player != null) {
(player as CraftPlayer).handle.playerConnection.sendPacket(packet)
} else {
for (player1 in this.getPlayers()) {
(player1 as CraftPlayer).handle.playerConnection.sendPacket(packet)
}
}
} catch (e: ReflectiveOperationException) {
throw RuntimeException(e)
}
}
override fun getMaxHealth(): Float {
return 100F
}
override fun setHealth(percentage: Float) {
setProgress(percentage / 100F)
}
override fun getHealth(): Float {
return getProgress() * 100F
}
override fun getReceiver(): Player {
throw UnsupportedOperationException()
}
override fun getLocation(): Location {
throw UnsupportedOperationException()
}
override fun updateMovement() {
throw UnsupportedOperationException()
}
}```

You can fix this specific case by doing
private fun argTypes(vararg args: Any): Array<Class<*>> {
val argTypes = args.map { it::class.javaPrimitiveType ?: it::class.java }
return argTypes.toTypedArray()
}
but then this wouldn't find a constructor which expects java.lang.Float.
Simply put: if you know the actual arguments for a constructor/method, there are many options for the types in the signature, so trying just one (as fun argTypes does) can't possibly work in general. In addition to the problem you ran into, the declared parameter type could be a supertype of an argument's class, the constructor could have varargs, etc.
A possible solution is just to get all constructors, feed your arguments to them and use the first one which doesn't throw an exception. You still need to be careful about varargs. Whether the performance cost is acceptable will depend on your use-case.

Related

Filter searchView from RecycleView with Adapter

Adapter class
class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) : RecyclerView.Adapter<AppListAdapter.AppViewHolder>() {
public val appList = arrayListOf<ApplicationInfo>()
private val checkedAppList = arrayListOf<Boolean>()
private val packageManager: PackageManager = context.packageManager
init {
context.packageManager.getInstalledApplications(PackageManager.GET_META_DATA).sortedBy { it.loadLabel(packageManager).toString() }.forEach { info ->
if (info.packageName != context.packageName) {
if (info.flags and ApplicationInfo.FLAG_SYSTEM == 0) {
appList.add(info)
checkedAppList.add(initialChecked.contains(info.packageName))
}
}
}
}
inner class AppViewHolder(private val item: ItemAppBinding) : RecyclerView.ViewHolder(item.root) {
fun bind(data: ApplicationInfo, position: Int) {
item.txApp.text = data.loadLabel(packageManager)
item.imgIcon.setImageDrawable(data.loadIcon(packageManager))
item.cbApp.isChecked = checkedAppList[position]
item.cbApp.setOnCheckedChangeListener { _, checked ->
checkedAppList[position] = checked
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AppViewHolder {
return AppViewHolder(ItemAppBinding.inflate(LayoutInflater.from(context), parent, false))
}
override fun onBindViewHolder(holder: AppViewHolder, position: Int) {
holder.bind(appList[position], position)
}
override fun getItemCount(): Int {
return appList.size
}
on MainActivity
binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
binding.searchView2.clearFocus()
// how to write code filtered by query?
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
// how to write code filtered by newText?
return false
}
})
I'm newbie in kotlin..anyone can help?
I believe you want to filter items with "ApplicationInfo.txApp", so i will write the code for it.
First you need your adapter class to extend Filterable like below, and add one more list to hold all items:
class AppListAdapter(private val context: Context, initialChecked: ArrayList<String> = arrayListOf()) : RecyclerView.Adapter<AppListAdapter.AppViewHolder>(), Filterable {
public val appList = arrayListOf<ApplicationInfo>()
public val appListFull = ArrayList<ApplicationInfo>(appList)
// This full list because of when you delete all the typing to searchView
// so it will get back to that full form.
Then override it's function and write your own filter to work, paste this code to your adapter:
override fun getFilter(): Filter {
return exampleFilter
}
private val exampleFilter = object : Filter() {
override fun performFiltering(constraint: CharSequence?): FilterResults? {
val filteredList: ArrayList<ApplicationInfo> = ArrayList()
if (constraint == null || constraint.isEmpty()) {
// when searchview is empty
filteredList.addAll(appListFull)
} else {
// when you type something
// it also uses Locale in case capital letters different.
val filterPattern = constraint.toString().lowercase(Locale.getDefault()).trim()
for (item in appListFull) {
val txApp = item.txApp
if (txApp.lowercase(Locale.getDefault()).contains(filterPattern)) {
filteredList.add(item)
}
}
}
val results = FilterResults()
results.values = filteredList
return results
}
#SuppressLint("NotifyDataSetChanged") #Suppress("UNCHECKED_CAST")
override fun publishResults(constraint: CharSequence?, results: FilterResults?) {
appList.clear()
appList.addAll(results!!.values as ArrayList<ApplicationInfo>)
notifyDataSetChanged()
}
}
And finally call this filter method in your searchview:
binding.searchView2.setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
yourAdapter.filter.filter(query)
yourAdapter.notifyDataSetChanged()
binding.searchView2.clearFocus()
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
yourAdapter.filter.filter(newText)
yourAdapter.notifyDataSetChanged()
return false
}
})
These should work i'm using something similar to that, if not let me know with the problem.

Unresolved reference: myViewHolder after switching to View Binding

After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
MyAdapter
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
private var touchHelper: ItemTouchHelper? = null
private var list = mutableListOf<MyObject>()
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
initList()
super.onAttachedToRecyclerView(recyclerView)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
return MyViewHolder(CellCardsBinding.inflate(LayoutInflater.from(parent.context), parent, false))
}
#RequiresApi(Build.VERSION_CODES.P)
#SuppressLint("ClickableViewAccessibility")
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
myViewHolder.bind(myObject)
val activity: Activity = context as Activity
holder.binding.text.setOnClickListener{
activity.launchActivity<AddNoteActivity>(42) {
putExtra("PositionInList", position.toString())
putExtra("TaskTitle", myObject.title)
putExtra("TaskText", myObject.text)
}
}
activity.findViewById<RecyclerView>(R.id.recyclerView).setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_UP -> {
updateNotesPositionInDb()
false
}
else -> {
false
}
}
}
holder.binding.title.setOnTouchListener { _, event ->
when (event.actionMasked) {
MotionEvent.ACTION_DOWN -> {
touchHelper!!.startDrag(holder)
false
}
else -> {
false
}
}
}
}
private fun initList() {
list.clear()
val db = mHelper.readableDatabase
val cursor = db.query(
TaskContract.TaskEntry.TABLE,
arrayOf(
TaskContract.TaskEntry.ID,
TaskContract.TaskEntry.COL_TASK_TITLE,
TaskContract.TaskEntry.COL_TASK_TEXT,
TaskContract.TaskEntry.COL_TASK_DATE),null, null, null, null, TaskContract.TaskEntry.ID)
while (cursor.moveToNext()) {
val id = cursor.getColumnIndex(TaskContract.TaskEntry.ID)
val idTitle = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TITLE)
val idText = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_TEXT)
val idDate = cursor.getColumnIndex(TaskContract.TaskEntry.COL_TASK_DATE)
list.add(MyObject(cursor.getString(id), cursor.getString(idTitle), cursor.getString(idText), cursor.getString(idDate)))
}
notifyDataSetChanged()
cursor.close()
db.close()
}
override fun getItemCount(): Int {
return list.size
}
override fun onViewMoved(oldPosition: Int, newPosition: Int) {
val target = list[oldPosition]
list.removeAt(oldPosition)
list.add(newPosition, target)
notifyItemMoved(oldPosition, newPosition)
}
override fun onViewSwiped(position: Int) {
deleteTask(list[position].ID)
list.removeAt(position)
notifyItemRemoved(position)
updateNotesPositionInDb()
}
fun setTouchHelper(touchHelper: ItemTouchHelper) {
this.touchHelper = touchHelper
}
fun addTask(taskTitle : String, taskText: String) {
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, list.size)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.insertWithOnConflict(TaskContract.TaskEntry.TABLE,
null,
values,
SQLiteDatabase.CONFLICT_REPLACE)
db.close()
list.add(MyObject(list.size.toString(), taskTitle, taskText, date))
notifyItemInserted(list.size)
}
fun addTask() {
val test: Activity = context as Activity
test.launchActivity<AddNoteActivity>(42) {
/* putExtra("user", "854")
p utExtra("user2", "46850") */
}
}
private fun deleteTask(taskId: String) {
val db = mHelper.readableDatabase
db.delete(TaskContract.TaskEntry.TABLE,
"id=$taskId", null)
db.close()
}
fun modifyTask(taskPosition: String, taskTitle: String, taskText: String) {
val target = list[taskPosition.toInt()]
target.title = taskTitle
target.text = taskText
val values = ContentValues()
val sdf = SimpleDateFormat("dd/MM/yyyy/", Locale.US)
val date = sdf.format(Date())
values.put(TaskContract.TaskEntry.ID, taskPosition)
values.put(TaskContract.TaskEntry.COL_TASK_TITLE, taskTitle)
values.put(TaskContract.TaskEntry.COL_TASK_TEXT, taskText)
values.put(TaskContract.TaskEntry.COL_TASK_DATE, date)
val db = mHelper.readableDatabase
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=" + target.ID, null)
db.close()
notifyItemChanged(taskPosition.toInt())
}
private fun updateNotesPositionInDb() {
val db = mHelper.readableDatabase
var i = 0
while (i < list.size) {
val values = ContentValues()
values.put(TaskContract.TaskEntry.ID, i)
db.update(TaskContract.TaskEntry.TABLE,
values, TaskContract.TaskEntry.ID + "=? AND " + TaskContract.TaskEntry.COL_TASK_TITLE + "=?", arrayOf(list[i].ID, list[i].title))
i++
}
db.close()
}
I've tried reading Android Studio's official documentation, but it cannot solve my specific problem.
in your class MyViewHolder you have method called binding and you need also to implement it and add paramter
shoud be
class MyViewHolder(private val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(data:MyObject) {
binding.yourView=data.title ...
}
}
in onBindViewHolder
..
holder.bind(myObject)
After switching from removing kotlin_extensions and switching to view binding, I received a "Unresolved reference: myViewHolder" in my onBindViewHolder method
Well, your onBindViewHolder method is passing a variable called holder and you're trying to use a variable called myViewHolder, so that seems like a problem.
// --------------------this-----v
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
// v--- doesn't match this
myViewHolder.bind(myObject)
and when I replace "myViewHolder" with "holder", it then gives me a "Unresolved reference: bind". How do I resolve this.
Your MyViewHolder class has a method called binding that takes no arguments. There is no bind method that takes a "myObject".
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun binding() {
}
}
Edit
You should pass an instance of the data class
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
fun bind(object: MyObject) {
// Set variables on binding
}
}
Then pass an instance from your list via onBindViewHolder:
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val myObject = list[position]
holder.bind(myObject)
Please check this blog post for more.
I got my answer.
class MyAdapter(private val context: Context, private val mHelper : TaskDbHelper) : RecyclerView.Adapter<MyAdapter.MyViewHolder>(),
SwipeAndDragHelper.ActionCompletionContract {
class MyViewHolder(val binding: CellCardsBinding): RecyclerView.ViewHolder(binding.root ) {
private val titleView: TextView = itemView.findViewById<View>(R.id.title) as TextView
val textView: TextView = itemView.findViewById<View>(R.id.text) as TextView
private val dateTextView: TextView = itemView.findViewById<View>(R.id.date) as TextView
fun binding (myObject: MyObject) {
titleView.text = myObject.title
textView.text = myObject.text
dateTextView.text = myObject.date
}
}
I simply initialised the view I wanted to reference in my layout and called them in the binding() function.

How to set up Viewmodel class properly?

I'm trying to follow some tutotial from github about MVVM model and i'm stuck at viewmodel class because there's an error says
Not enough information to infer type variable T
and
Type mismatch.
Required:Resource<Movie>
Found:Unit
And when i check my other class like ApiService, Dao, NetworkBoundResource, ApiResponse, Resources and respository everthing fine like this
ApiService :
interface ApiService {
#GET("3/movie/popular")
fun getMyMovie(#Query("api_key") api : String = "32bbbffe944d16d1d2a3ee46cfc6aaa0"
) : Flow<ApiResponse<MovieResponse.Movie>>
}
MovieDao:
#Dao
interface MovieDao : BaseDao<Movie> {
// #Insert(onConflict = OnConflictStrategy.REPLACE)
// fun insertMovie(movie: List<Movie>)
#Query("SELECT * FROM `movie` ORDER by movie_id DESC")
fun getMyMovie() : Flow<Movie>
#Query("SELECT * FROM `movie` ORDER by movie_id DESC")
fun findAllMovie() : Maybe<List<Movie>>
#Query("SELECT * FROM `movie` ORDER by movie_id DESC")
fun streamAll() : Flowable<List<Movie>>
#Query("DELETE FROM `movie`")
fun deleteAll()
}
MovieRespository:
class MovieRespository (val apiService: ApiService, val movieDao: MovieDao) {
fun getListMovie() : Flow<Resource<Movie>> {
return networkBoundResource(
fetchFromLocal = { movieDao.getMyMovie() },
shouldFetchFromRemote = {true},
fetchFromRemote = {apiService.getMyMovie()},
processRemoteResponse = {},
saveRemoteData = {movieDao.insert(
it.results.let {
it.map { data -> Movie.from(data) }
}
)},
onFetchFailed = {_, _ ->}
).flowOn(Dispatchers.IO)
}
NeteorkBoundResource:
inline fun <DB, REMOTE> networkBoundResource(
crossinline fetchFromLocal: () -> Flow<DB>,
crossinline shouldFetchFromRemote: (DB?) -> Boolean = { true },
crossinline fetchFromRemote: () -> Flow<ApiResponse<REMOTE>>,
crossinline processRemoteResponse: (response: ApiSuccessResponse<REMOTE>) -> Unit = { Unit },
crossinline saveRemoteData: (REMOTE) -> Unit = { Unit },
crossinline onFetchFailed: (errorBody: String?, statusCode: Int) -> Unit = { _: String?, _: Int -> Unit }
) = flow<Resource<DB>> {
emit(Resource.Loading(null))
val localData = fetchFromLocal().first()
if (shouldFetchFromRemote(localData)) {
emit(Resource.Loading(localData))
fetchFromRemote().collect { apiResponse ->
when (apiResponse) {
is ApiSuccessResponse -> {
processRemoteResponse(apiResponse)
apiResponse.body?.let { saveRemoteData(it) }
emitAll(fetchFromLocal().map { dbData ->
Resource.Success(dbData)
})
}
is ApiErrorResponse -> {
onFetchFailed(apiResponse.errorMessage, apiResponse.statusCode)
emitAll(fetchFromLocal().map {
Resource.Error(
apiResponse.errorMessage,
it
)
})
}
}
}
} else {
emitAll(fetchFromLocal().map { Resource.Success(it) })
}
}
ApiResponse:
sealed class ApiResponse<T> {
companion object {
fun <T> create(error: Throwable): ApiErrorResponse<T> {
return ApiErrorResponse(
error.message ?: "Unknown error",
0
)
}
fun <T> create(response: Response<T>): ApiResponse<T> {
return if (response.isSuccessful) {
val body = response.body()
val headers = response.headers()
if (body == null || response.code() == 204) {
ApiEmptyResponse()
} else {
ApiSuccessResponse(
body,
headers
)
}
} else {
val msg = response.errorBody()?.string()
val errorMsg = if (msg.isNullOrEmpty()) {
response.message()
} else {
msg
}
ApiErrorResponse(
errorMsg ?: "Unknown error",
response.code()
)
}
}
}
}
/**
* separate class for HTTP 204 responses so that we can make ApiSuccessResponse's body non-null.
*/
class ApiEmptyResponse<T> : ApiResponse<T>()
data class ApiSuccessResponse<T>(
val body: T?,
val headers: okhttp3.Headers
) : ApiResponse<T>()
data class ApiErrorResponse<T>(val errorMessage: String, val statusCode: Int) : ApiResponse<T>()
Resource:
data class Resource<out T>(val status: Status, val data: T?, val message: String?) {
// data class Loading<T>(val loadingData: T?) : Resource<T>(Status.LOADING, loadingData, null)
// data class Success<T>(val successData: T?) : Resource<T>(Status.SUCCESS, successData, null)
// data class Error<T>(val msg: String, val error: T?) : Resource<T>(Status.ERROR, error, msg)
companion object {
fun <T> Success(data: T?): Resource<T> {
return Resource(Status.SUCCESS, data,null)
}
fun <T> Error(msg: String, data: T? = null): Resource<T> {
return Resource(Status.ERROR, data, msg)
}
fun <T> Loading(data: T? = null): Resource<T> {
return Resource(Status.LOADING, data, null)
}
}
}
MainViewModel:
class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
#ExperimentalCoroutinesApi
val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
when(it.status){
Resource.Loading() ->{}
Resource.Success() ->{}
Resource.Error() ->{}
}
}.asLiveData(viewModelScope.coroutineContext)
}
to be specific this what the error looks like and this is the link of tutorial i learn
https://github.com/hadiyarajesh/flower
viewModel Class Error
You get the error, because Inside the when, you are trying to construct a new instance of Resource.Loading() etc, but those require a type, so it would need to be something like Resource.Loading<Movie>().
Tht being said, you are doing when(it.status), so the cases in the when, should not be a Resource.Loading, but Status.LOADING instead:
class MainViewModel(private val movieRespository: MovieRespository) : ViewModel() {
#ExperimentalCoroutinesApi
val getListMovies: LiveData<Resource<Movie>> = movieRespository.getListMovie().map {
when(it.status){
Status.LOADING ->{}
Status.SUCCESS ->{}
Status.ERROR ->{}
}
return#map it
}.asLiveData(viewModelScope.coroutineContext)
}
Also, since you are declaring LiveData<Resource<Movie>>, you need to return a Resource<Movie> from the map {} (we could drop return#map, it is just to be explicit)

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) {
}
})

Invoking Action by reference in Kotlin

I've a Map of (key, value) where the value is a predefined function.
I want to iterate the input param in the Mp and check where the key is matching with the input parameter, then invoke the equivalent function, something like this
My code required to be something like below:
fun fn1: Unit { // using Unit is optional
println("Hi there!")
}
fun fn2 {
println("Hi again!")
}
fun MainFun(x: int){
val map: HashMap<Int, String> = hashMapOf(1 to fn1, 2 to fn2)
for ((key, value) in map) {
// if key = x then run/invoke the function mapped with x, for example if x = 1 then invoke fn1
}
}
Notes: I read something like below, but could not know how to us them:
inline fun <K, V> Map<out K, V>.filter(
predicate: (Entry<K, V>) -> Boolean
): Map<K, V> (source)
val russianNames = arrayOf("Maksim", "Artem", "Sophia", "Maria", "Maksim")
val selectedName = russianNames
.filter { it.startsWith("m", ignoreCase = true) }
.sortedBy { it.length }
.firstOrNull()
Hi I hope this would help you.
fun fn1() {
println("Hi there!")
}
fun fn2() {
println("Hi again!")
}
fun main(args: IntArray){
val map = hashMapOf(
1 to ::fn1,
2 to ::fn2)
map.filterKeys { it == args[0] } // filters the map by comparing the first int arg passed and the key
.map { it.value.invoke() } // invoke the function that passed the filter.
}
If the keyis RegEx then map.filterKeys { Regex(it).matches(x) } can be used, below full example of it Try Kotlin:
data class Person(val name: String,
val age: Int? = null)
val persons = listOf(Person("Alice"),
Person("Bob", age = 23))
fun old() {
val oldest = persons.maxBy { it.age ?: 0 }
println("The oldest is: $oldest")
}
fun young() {
val youngest = persons.minBy { it.age ?: 0 }
println("The youngest is: $youngest")
}
fun selection(x: String) {
val map = mapOf(
"old|big" to ::old,
"new|young" to ::young)
map.filterKeys { Regex(it).matches(x) }
.map { it.value.invoke() }
}
fun main(args: Array<String>) {
selection("new")
}
fun fn1() {
println("Hi there!")
}
fun fn2() {
println("Hi again!")
}
fun main(args: Array<Int>){
val map = hashMapOf(1 to ::fn1, 2 to ::fn2)
map.forEach { key, function -> function.invoke() }
}
This will do the work but your code does not even have the correct syntax. You should learn the basic first.