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.
I've got such code for searching streams by their name.
compositeDisposable.add(Observable.create(ObservableOnSubscribe<String> { subscriber ->
binding.searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextChange(newText: String?): Boolean {
subscriber.onNext(newText!!)
return false
}
override fun onQueryTextSubmit(query: String?): Boolean {
subscriber.onNext(query!!)
return false
}
})
})
.debounce(250, TimeUnit.MILLISECONDS)
.distinct()
.subscribe { text ->
val pagerPos = binding.streamsPager.currentItem
val currentFragment =
activity?.supportFragmentManager?.findFragmentByTag("f${pagerAdapter.getItemId(pagerPos)}")
(currentFragment as StreamFragment).onSearchHolder.onSearch(text)
}
)
It works fine with onQueryTextSubmit, but ignores setOnQueryTextListener. So when I want to erase symbols and "reset" search I get nothing
How to fix this?
use a wrapper class for your ObservableOnSubscribe and sealed class for your search events. Like this:
class SearchViewWrapper(private val searchView: SearchView) : ObservableOnSubscribe<SearchViewEvent> {
override fun subscribe(emitter: ObservableEmitter<SearchViewEvent>) {
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
emitter.onNext(SearchViewEvent.onQueryTextSumbit(query))
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
emitter.onNext(SearchViewEvent.onQueryChanged(newText))
return true
}
})
}
}
Sealed class for events:
sealed class SearchViewEvent {
data class onQueryChanged(val text: String?): SearchViewEvent()
data class onQueryTextSumbit(val query: String?): SearchViewEvent()
}
Try to use it where you need(just example):
fun useIt(searchView: SearchView) {
Observable.create(SearchViewWrapper(searchView))
.subscribe(::handleSearchViewEvent)
.addTo(yourCompositeDisposable)
}
private fun handleSearchViewEvent(event: SearchViewEvent) {
when (event) {
is SearchViewEvent.onQueryChanged -> TODO()
is SearchViewEvent.onQueryTextSumbit -> TODO()
}
}
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.