skyline/app/src/main/java/emu/skyline/adapter/HeaderAdapter.kt
◱ PixelyIon 55a9f8e937 Refactor all Game-related Kotlin Classes/Objects
This refactors most game-related classes and objects mainly adding spacing, adding comments and making improvements if possible.
2020-04-23 22:26:27 +05:30

175 lines
5.8 KiB
Kotlin

/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* Copyright © 2020 Skyline Team and Contributors (https://github.com/skyline-emu/)
*/
package emu.skyline.adapter
import android.util.SparseIntArray
import android.view.View
import android.view.ViewGroup
import android.widget.BaseAdapter
import android.widget.Filter
import android.widget.Filterable
import info.debatty.java.stringsimilarity.Cosine
import info.debatty.java.stringsimilarity.JaroWinkler
import java.io.*
import java.util.*
import kotlin.collections.ArrayList
enum class ElementType(val type: Int) {
Header(0x0),
Item(0x1)
}
/**
* This is an abstract class that all adapter element classes inherit from
*/
abstract class BaseElement constructor(val elementType: ElementType) : Serializable
/**
* This is an abstract class that all adapter header classes inherit from
*/
class BaseHeader constructor(val title: String) : BaseElement(ElementType.Header)
/**
* This is an abstract class that all adapter item classes inherit from
*/
abstract class BaseItem : BaseElement(ElementType.Item) {
/**
* This function returns a string used for searching
*/
abstract fun key(): String?
}
internal abstract class HeaderAdapter<ItemType : BaseItem?, HeaderType : BaseHeader?> : BaseAdapter(), Filterable, Serializable {
var elementArray: ArrayList<BaseElement?> = ArrayList()
var visibleArray: ArrayList<Int> = ArrayList()
private var searchTerm = ""
fun addItem(element: ItemType) {
elementArray.add(element)
if (searchTerm.isNotEmpty())
filter.filter(searchTerm)
else {
visibleArray.add(elementArray.size - 1)
notifyDataSetChanged()
}
}
fun addHeader(element: HeaderType) {
elementArray.add(element)
if (searchTerm.isNotEmpty())
filter.filter(searchTerm)
else {
visibleArray.add(elementArray.size - 1)
notifyDataSetChanged()
}
}
internal inner class State(val elementArray: ArrayList<BaseElement?>) : Serializable
@Throws(IOException::class)
fun save(file: File) {
val fileObj = FileOutputStream(file)
val out = ObjectOutputStream(fileObj)
out.writeObject(elementArray)
out.close()
fileObj.close()
}
@Throws(IOException::class, ClassNotFoundException::class)
open fun load(file: File) {
val fileObj = FileInputStream(file)
val input = ObjectInputStream(fileObj)
@Suppress("UNCHECKED_CAST")
elementArray = input.readObject() as ArrayList<BaseElement?>
input.close()
fileObj.close()
filter.filter(searchTerm)
}
fun clear() {
elementArray.clear()
visibleArray.clear()
notifyDataSetChanged()
}
override fun getCount(): Int {
return visibleArray.size
}
override fun getItem(index: Int): BaseElement? {
return elementArray[visibleArray[index]]
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return elementArray[visibleArray[position]]!!.elementType.type
}
override fun getViewTypeCount(): Int {
return ElementType.values().size
}
abstract override fun getView(position: Int, convertView: View?, parent: ViewGroup): View
override fun getFilter(): Filter {
return object : Filter() {
inner class ScoredItem(val score: Double, val index: Int, val item:String) {}
fun extractSorted(term: String, keyArray: ArrayList<String>): Array<ScoredItem> {
val scoredItems : MutableList<ScoredItem> = ArrayList()
val jw = JaroWinkler()
val cos = Cosine()
keyArray.forEachIndexed { index, item -> scoredItems.add(ScoredItem((jw.similarity(term, item) + cos.similarity(term, item)) / 2, index, item)) }
scoredItems.sortWith(compareByDescending { it.score })
return scoredItems.toTypedArray()
}
override fun performFiltering(charSequence: CharSequence): FilterResults {
val results = FilterResults()
searchTerm = (charSequence as String).toLowerCase(Locale.getDefault())
if (charSequence.isEmpty()) {
results.values = elementArray.indices.toMutableList()
results.count = elementArray.size
} else {
val filterData = ArrayList<Int>()
val keyArray = ArrayList<String>()
val keyIndex = SparseIntArray()
for (index in elementArray.indices) {
val item = elementArray[index]!!
if (item is BaseItem) {
keyIndex.append(keyArray.size, index)
keyArray.add(item.key()!!.toLowerCase(Locale.getDefault()))
}
}
val topResults = extractSorted(searchTerm, keyArray)
val avgScore = topResults.sumByDouble { it.score } / topResults.size
for (result in topResults)
if (result.score > avgScore)
filterData.add(keyIndex[result.index])
results.values = filterData
results.count = filterData.size
}
return results
}
override fun publishResults(charSequence: CharSequence, results: FilterResults) {
if (results.values is ArrayList<*>) {
@Suppress("UNCHECKED_CAST")
visibleArray = results.values as ArrayList<Int>
notifyDataSetChanged()
}
}
}
}
}