[UI/Messages] Restore search string after closing a message. Add message counter.

This commit is contained in:
Kuba Szczodrzyński 2021-04-14 20:19:04 +02:00
parent b48b5589f4
commit 1e8fb6a9ae
No known key found for this signature in database
GPG Key ID: 70CB8A85BA1633CB
10 changed files with 343 additions and 239 deletions

View File

@ -15,6 +15,7 @@ import android.graphics.drawable.Drawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.* import android.text.*
import android.text.style.CharacterStyle
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.StrikethroughSpan import android.text.style.StrikethroughSpan
import android.text.style.StyleSpan import android.text.style.StyleSpan
@ -552,28 +553,46 @@ fun CharSequence?.asBoldSpannable(): Spannable {
spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(StyleSpan(Typeface.BOLD), 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
return spannable return spannable
} }
fun CharSequence.asSpannable(vararg spans: Any, substring: String? = null, ignoreCase: Boolean = false, ignoreDiacritics: Boolean = false): Spannable { fun CharSequence.asSpannable(
vararg spans: CharacterStyle,
substring: CharSequence? = null,
ignoreCase: Boolean = false,
ignoreDiacritics: Boolean = false
): Spannable {
val spannable = SpannableString(this) val spannable = SpannableString(this)
if (substring == null) { substring?.let { substr ->
spans.forEach { val string = if (ignoreDiacritics)
spannable.setSpan(it, 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
else if (substring.isNotEmpty()) {
val string =
if (ignoreDiacritics)
this.cleanDiacritics() this.cleanDiacritics()
else this else
this
val search = if (ignoreDiacritics)
substr.cleanDiacritics()
else
substr.toString()
var index = string.indexOf(substring, ignoreCase = ignoreCase) var index = 0
.takeIf { it != -1 } ?: indexOf(substring, ignoreCase = ignoreCase) do {
while (index >= 0) { index = string.indexOf(
string = search,
startIndex = index,
ignoreCase = ignoreCase
)
if (index >= 0) {
spans.forEach { spans.forEach {
spannable.setSpan(it, index, index + substring.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) spannable.setSpan(
CharacterStyle.wrap(it),
index,
index + substring.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
} }
index = string.indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase) index += substring.length.coerceAtLeast(1)
.takeIf { it != -1 } ?: indexOf(substring, startIndex = index + 1, ignoreCase = ignoreCase)
} }
} while (index >= 0)
} ?: spans.forEach {
spannable.setSpan(it, 0, spannable.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
} }
return spannable return spannable
} }

View File

@ -30,7 +30,7 @@ class MessageFull(
@Ignore @Ignore
var filterWeight = 0 var filterWeight = 0
@Ignore @Ignore
var searchHighlightText: String? = null var searchHighlightText: CharSequence? = null
// metadata // metadata
var seen = false var seen = false

View File

@ -1,11 +1,8 @@
package pl.szczodrzynski.edziennik.ui.modules.messages package pl.szczodrzynski.edziennik.ui.modules.messages
import android.graphics.Typeface import android.graphics.Typeface
import android.text.Editable
import android.text.TextWatcher
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Filter
import android.widget.Filterable import android.widget.Filterable
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -13,17 +10,14 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.cleanDiacritics
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.entity.Teacher import pl.szczodrzynski.edziennik.data.db.entity.Teacher
import pl.szczodrzynski.edziennik.data.db.full.MessageFull import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
import pl.szczodrzynski.edziennik.ui.modules.messages.utils.MessagesFilter
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.MessageViewHolder import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.MessageViewHolder
import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.SearchViewHolder import pl.szczodrzynski.edziennik.ui.modules.messages.viewholder.SearchViewHolder
import java.util.*
import kotlin.coroutines.CoroutineContext import kotlin.coroutines.CoroutineContext
import kotlin.math.min
class MessagesAdapter( class MessagesAdapter(
val activity: AppCompatActivity, val activity: AppCompatActivity,
@ -43,41 +37,10 @@ class MessagesAdapter(
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main get() = job + Dispatchers.Main
var items = mutableListOf<Any>() var items = listOf<Any>()
var allItems = mutableListOf<Any>() var allItems = listOf<Any>()
val typefaceNormal: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) } val typefaceNormal: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.NORMAL) }
val typefaceBold: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) } val typefaceBold: Typeface by lazy { Typeface.create(Typeface.DEFAULT, Typeface.BOLD) }
private val comparator by lazy { Comparator { o1: Any, o2: Any ->
if (o1 !is MessageFull || o2 !is MessageFull)
return@Comparator 0
when {
// standard sorting
o1.filterWeight > o2.filterWeight -> return@Comparator 1
o1.filterWeight < o2.filterWeight -> return@Comparator -1
else -> when {
// reversed sorting
o1.addedDate > o2.addedDate -> return@Comparator -1
o1.addedDate < o2.addedDate -> return@Comparator 1
else -> return@Comparator 0
}
}
}}
val textWatcher by lazy {
object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
getFilter().filter(s.toString())
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
/*items.getOrNull(0)?.let {
if (it is MessagesSearch) {
it.searchText = s?.toString() ?: ""
}
}*/
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
@ -103,138 +66,16 @@ class MessagesAdapter(
return return
when { when {
holder is MessageViewHolder && item is MessageFull -> holder.onBind(activity, app, item, position, this) holder is MessageViewHolder
holder is SearchViewHolder && item is MessagesSearch -> holder.onBind(activity, app, item, position, this) && item is MessageFull -> holder.onBind(activity, app, item, position, this)
holder is SearchViewHolder
&& item is MessagesSearch -> holder.onBind(activity, app, item, position, this)
} }
} }
private val messagesFilter by lazy {
MessagesFilter(this)
}
override fun getItemCount() = items.size override fun getItemCount() = items.size
override fun getFilter() = filter override fun getFilter() = messagesFilter
private var prevCount = -1
private val filter by lazy { object : Filter() {
override fun performFiltering(prefix: CharSequence?): FilterResults {
val results = FilterResults()
if (prevCount == -1)
prevCount = allItems.size
if (prefix.isNullOrEmpty()) {
allItems.forEach {
if (it is MessageFull)
it.searchHighlightText = null
}
results.values = allItems.toList()
results.count = allItems.size
return results
}
val items = mutableListOf<Any>()
val prefixString = prefix.toString()
allItems.forEach {
if (it !is MessageFull) {
items.add(it)
return@forEach
}
it.filterWeight = 100
it.searchHighlightText = null
var weight: Int
if (it.type == Message.TYPE_SENT) {
it.recipients?.forEach { recipient ->
weight = getMatchWeight(recipient.fullName, prefixString)
if (weight != 100) {
if (weight == 3)
weight = 31
it.filterWeight = min(it.filterWeight, 10 + weight)
}
}
}
else {
weight = getMatchWeight(it.senderName, prefixString)
if (weight != 100) {
if (weight == 3)
weight = 31
it.filterWeight = min(it.filterWeight, 10 + weight)
}
}
weight = getMatchWeight(it.subject, prefixString)
if (weight != 100) {
if (weight == 3)
weight = 22
it.filterWeight = min(it.filterWeight, 20 + weight)
}
if (it.filterWeight != 100) {
it.searchHighlightText = prefixString
items.add(it)
}
}
Collections.sort(items, comparator)
results.values = items
results.count = items.size
return results
}
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
results.values?.let { items = it as MutableList<Any> }
// do not re-bind the search box
val count = results.count - 1
// this tries to update every item except the search field
when {
count > prevCount -> {
notifyItemRangeInserted(prevCount + 1, count - prevCount)
notifyItemRangeChanged(1, prevCount)
}
count < prevCount -> {
notifyItemRangeRemoved(prevCount + 1, prevCount - count)
notifyItemRangeChanged(1, count)
}
else -> {
notifyItemRangeChanged(1, count)
}
}
/*if (prevCount != count) {
items.getOrNull(0)?.let {
if (it is MessagesSearch) {
it.count = count
notifyItemChanged(0)
}
}
}*/
prevCount = count
}
}}
private fun getMatchWeight(name: CharSequence?, prefix: String): Int {
if (name == null)
return 100
val nameClean = name.cleanDiacritics()
// First match against the whole, non-split value
if (nameClean.startsWith(prefix, ignoreCase = true) || name.startsWith(prefix, ignoreCase = true)) {
return 1
} else {
// check if prefix matches any of the words
val words = nameClean.split(" ").toTypedArray() + name.split(" ").toTypedArray()
for (word in words) {
if (word.startsWith(prefix, ignoreCase = true)) {
return 2
}
}
}
// finally check if the prefix matches any part of the name
if (nameClean.contains(prefix, ignoreCase = true) || name.contains(prefix, ignoreCase = true)) {
return 3
}
return 100
}
} }

View File

@ -33,6 +33,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
private lateinit var app: App private lateinit var app: App
private lateinit var activity: MainActivity private lateinit var activity: MainActivity
private lateinit var b: MessagesListFragmentBinding private lateinit var b: MessagesListFragmentBinding
private var adapter: MessagesAdapter? = null
private val job: Job = Job() private val job: Job = Job()
override val coroutineContext: CoroutineContext override val coroutineContext: CoroutineContext
@ -53,21 +54,22 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
val messageType = arguments.getInt("messageType", Message.TYPE_RECEIVED) val messageType = arguments.getInt("messageType", Message.TYPE_RECEIVED)
var topPosition = arguments.getInt("topPosition", NO_POSITION) var topPosition = arguments.getInt("topPosition", NO_POSITION)
var bottomPosition = arguments.getInt("bottomPosition", NO_POSITION) var bottomPosition = arguments.getInt("bottomPosition", NO_POSITION)
val searchText = arguments.getString("searchText", "")
teachers = withContext(Dispatchers.Default) { teachers = withContext(Dispatchers.Default) {
app.db.teacherDao().getAllNow(App.profileId) app.db.teacherDao().getAllNow(App.profileId)
} }
val adapter = MessagesAdapter(activity, teachers) { adapter = MessagesAdapter(activity, teachers) {
activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, Bundle( activity.loadTarget(MainActivity.TARGET_MESSAGES_DETAILS, Bundle(
"messageId" to it.id "messageId" to it.id
)) ))
} }
app.db.messageDao().getAllByType(App.profileId, messageType).observe(this@MessagesListFragment, Observer { items -> app.db.messageDao().getAllByType(App.profileId, messageType).observe(this@MessagesListFragment, Observer { messages ->
if (!isAdded) return@Observer if (!isAdded) return@Observer
items.forEach { message -> messages.forEach { message ->
message.recipients?.removeAll { it.profileId != message.profileId } message.recipients?.removeAll { it.profileId != message.profileId }
message.recipients?.forEach { recipient -> message.recipients?.forEach { recipient ->
if (recipient.fullName == null) { if (recipient.fullName == null) {
@ -77,13 +79,22 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
} }
// load & configure the adapter // load & configure the adapter
adapter.items = items.toMutableList() val items = messages.toMutableList<Any>()
adapter.items.add(0, MessagesSearch().also { items.add(0, MessagesSearch().also {
it.count = items.size it.searchText = searchText
}) })
adapter.allItems = adapter.items.toMutableList()
adapter?.items = items
adapter?.allItems = items
if (items.isNotNullNorEmpty() && b.list.adapter == null) { if (items.isNotNullNorEmpty() && b.list.adapter == null) {
if (searchText.isNotBlank())
adapter?.filter?.filter(searchText) {
b.list.adapter = adapter b.list.adapter = adapter
}
else
b.list.adapter = adapter
b.list.apply { b.list.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = LinearLayoutManager(context) layoutManager = LinearLayoutManager(context)
@ -92,7 +103,7 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
addOnScrollListener(onScrollListener) addOnScrollListener(onScrollListener)
} }
} }
adapter.notifyDataSetChanged()
setSwipeToRefresh(messageType in Message.TYPE_RECEIVED..Message.TYPE_SENT && items.isNullOrEmpty()) setSwipeToRefresh(messageType in Message.TYPE_RECEIVED..Message.TYPE_SENT && items.isNullOrEmpty())
(b.list.layoutManager as? LinearLayoutManager)?.let { layoutManager -> (b.list.layoutManager as? LinearLayoutManager)?.let { layoutManager ->
@ -119,10 +130,15 @@ class MessagesListFragment : LazyFragment(), CoroutineScope {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
if (!isAdded) return if (!isAdded)
return
val layoutManager = (b.list.layoutManager as? LinearLayoutManager)
val searchItem = adapter?.items?.firstOrNull { it is MessagesSearch } as? MessagesSearch
onPageDestroy?.invoke(position, Bundle( onPageDestroy?.invoke(position, Bundle(
"topPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition(), "topPosition" to layoutManager?.findFirstVisibleItemPosition(),
"bottomPosition" to (b.list.layoutManager as? LinearLayoutManager)?.findLastCompletelyVisibleItemPosition() "bottomPosition" to layoutManager?.findLastCompletelyVisibleItemPosition(),
"searchText" to searchItem?.searchText?.toString()
)) ))
} }
} }

View File

@ -5,7 +5,5 @@
package pl.szczodrzynski.edziennik.ui.modules.messages.models package pl.szczodrzynski.edziennik.ui.modules.messages.models
class MessagesSearch { class MessagesSearch {
var isFocused = false var searchText: CharSequence = ""
var searchText = ""
var count = 0
} }

View File

@ -0,0 +1,27 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
class MessagesComparator : Comparator<Any> {
override fun compare(o1: Any?, o2: Any?): Int {
if (o1 !is MessageFull || o2 !is MessageFull)
return 0
return when {
// standard sorting
o1.filterWeight > o2.filterWeight -> 1
o1.filterWeight < o2.filterWeight -> -1
else -> when {
// reversed sorting
o1.addedDate > o2.addedDate -> -1
o1.addedDate < o2.addedDate -> 1
else -> 0
}
}
}
}

View File

@ -0,0 +1,149 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
import android.widget.Filter
import pl.szczodrzynski.edziennik.cleanDiacritics
import pl.szczodrzynski.edziennik.data.db.entity.Message
import pl.szczodrzynski.edziennik.data.db.full.MessageFull
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
import java.util.*
import kotlin.math.min
class MessagesFilter(
private val adapter: MessagesAdapter
) : Filter() {
companion object {
private const val NO_MATCH = 1000
}
private val comparator = MessagesComparator()
private var prevCount = -1
private val allItems
get() = adapter.allItems
private fun getMatchWeight(name: CharSequence?, prefix: CharSequence): Int {
if (name == null)
return NO_MATCH
val prefixClean = prefix.cleanDiacritics()
val nameClean = name.cleanDiacritics()
return when {
// First match against the whole, non-split value
nameClean.startsWith(prefixClean, ignoreCase = true) -> 1
// check if prefix matches any of the words
nameClean.split(" ").any {
it.startsWith(prefixClean, ignoreCase = true)
} -> 2
// finally check if the prefix matches any part of the name
nameClean.contains(prefixClean, ignoreCase = true) -> 3
else -> NO_MATCH
}
}
override fun performFiltering(prefix: CharSequence?): FilterResults {
val results = FilterResults()
if (prevCount == -1)
prevCount = allItems.size
if (prefix.isNullOrBlank()) {
allItems.forEach {
if (it is MessageFull)
it.searchHighlightText = null
}
results.values = allItems.toList()
results.count = allItems.size
return results
}
val items = mutableListOf<Any>()
allItems.forEach {
if (it !is MessageFull) {
items.add(it)
return@forEach
}
it.filterWeight = NO_MATCH
it.searchHighlightText = null
var weight: Int
// weights 11..13 and 110
if (it.type == Message.TYPE_SENT) {
it.recipients?.forEach { recipient ->
weight = getMatchWeight(recipient.fullName, prefix)
if (weight != NO_MATCH) {
if (weight == 3)
weight = 100
it.filterWeight = min(it.filterWeight, 10 + weight)
}
}
} else {
weight = getMatchWeight(it.senderName, prefix)
if (weight != NO_MATCH) {
if (weight == 3)
weight = 100
it.filterWeight = min(it.filterWeight, 10 + weight)
}
}
// weights 21..23 and 120
weight = getMatchWeight(it.subject, prefix)
if (weight != NO_MATCH) {
if (weight == 3)
weight = 100
it.filterWeight = min(it.filterWeight, 20 + weight)
}
// weights 31..33 and 130
weight = getMatchWeight(it.body, prefix)
if (weight != NO_MATCH) {
if (weight == 3)
weight = 100
it.filterWeight = min(it.filterWeight, 30 + weight)
}
if (it.filterWeight != NO_MATCH) {
it.searchHighlightText = prefix
items.add(it)
}
}
Collections.sort(items, comparator)
results.values = items
results.count = items.size
return results
}
override fun publishResults(constraint: CharSequence?, results: FilterResults) {
results.values?.let {
adapter.items = it as MutableList<Any>
}
// do not re-bind the search box
val count = results.count - 1
// this tries to update every item except the search field
with(adapter) {
when {
count > prevCount -> {
notifyItemRangeInserted(prevCount + 1, count - prevCount)
notifyItemRangeChanged(1, prevCount)
}
count < prevCount -> {
notifyItemRangeRemoved(prevCount + 1, prevCount - count)
notifyItemRangeChanged(1, count)
}
else -> {
notifyItemRangeChanged(1, count)
}
}
}
prevCount = count
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (c) Kuba Szczodrzyński 2021-4-14.
*/
package pl.szczodrzynski.edziennik.ui.modules.messages.utils
import android.text.Editable
import android.text.TextWatcher
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
class SearchTextWatcher(
private val b: MessagesListItemSearchBinding,
private val filter: MessagesFilter,
private val item: MessagesSearch
) : TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
override fun afterTextChanged(s: Editable?) {
item.searchText = s ?: ""
filter.filter(s) { count ->
if (s.isNullOrBlank())
b.searchLayout.helperText = " "
else
b.searchLayout.helperText =
b.root.context.getString(R.string.messages_search_results, count - 1)
}
}
override fun equals(other: Any?): Boolean {
return other is SearchTextWatcher
}
override fun hashCode(): Int {
var result = b.hashCode()
result = 31 * result + filter.hashCode()
result = 31 * result + item.hashCode()
return result
}
}

View File

@ -30,9 +30,13 @@ class MessageViewHolder(
private const val TAG = "MessageViewHolder" private const val TAG = "MessageViewHolder"
} }
override fun onBind(activity: AppCompatActivity, app: App, item: MessageFull, position: Int, adapter: MessagesAdapter) { override fun onBind(
val manager = app.gradesManager activity: AppCompatActivity,
app: App,
item: MessageFull,
position: Int,
adapter: MessagesAdapter
) {
b.messageSubject.text = item.subject b.messageSubject.text = item.subject
b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort b.messageDate.text = Date.fromMillis(item.addedDate).formattedStringShort
b.messageAttachmentImage.isVisible = item.hasAttachments b.messageAttachmentImage.isVisible = item.hasAttachments
@ -55,15 +59,17 @@ class MessageViewHolder(
b.messageProfileBackground.setImageBitmap(messageInfo.profileImage) b.messageProfileBackground.setImageBitmap(messageInfo.profileImage)
b.messageSender.text = messageInfo.profileName b.messageSender.text = messageInfo.profileName
item.searchHighlightText?.let { highlight -> item.searchHighlightText?.toString()?.let { highlight ->
val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity) val colorHighlight = R.attr.colorControlHighlight.resolveAttr(activity)
b.messageSubject.text = b.messageSubject.text.asSpannable( b.messageSubject.text = b.messageSubject.text.asSpannable(
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight), StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
substring = highlight, ignoreCase = true, ignoreDiacritics = true) substring = highlight, ignoreCase = true, ignoreDiacritics = true
)
b.messageSender.text = b.messageSender.text.asSpannable( b.messageSender.text = b.messageSender.text.asSpannable(
StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight), StyleSpan(Typeface.BOLD), BackgroundColorSpan(colorHighlight),
substring = highlight, ignoreCase = true, ignoreDiacritics = true) substring = highlight, ignoreCase = true, ignoreDiacritics = true
)
} }
adapter.onItemClick?.let { listener -> adapter.onItemClick?.let { listener ->

View File

@ -9,38 +9,43 @@ import android.view.ViewGroup
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import pl.szczodrzynski.edziennik.App import pl.szczodrzynski.edziennik.App
import pl.szczodrzynski.edziennik.R
import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding import pl.szczodrzynski.edziennik.databinding.MessagesListItemSearchBinding
import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder import pl.szczodrzynski.edziennik.ui.modules.grades.viewholder.BindableViewHolder
import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter import pl.szczodrzynski.edziennik.ui.modules.messages.MessagesAdapter
import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch import pl.szczodrzynski.edziennik.ui.modules.messages.models.MessagesSearch
import pl.szczodrzynski.edziennik.ui.modules.messages.utils.SearchTextWatcher
class SearchViewHolder( class SearchViewHolder(
inflater: LayoutInflater, inflater: LayoutInflater,
parent: ViewGroup, parent: ViewGroup,
val b: MessagesListItemSearchBinding = MessagesListItemSearchBinding.inflate(inflater, parent, false) val b: MessagesListItemSearchBinding = MessagesListItemSearchBinding.inflate(
inflater,
parent,
false
)
) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessagesSearch, MessagesAdapter> { ) : RecyclerView.ViewHolder(b.root), BindableViewHolder<MessagesSearch, MessagesAdapter> {
companion object { companion object {
private const val TAG = "SearchViewHolder" private const val TAG = "SearchViewHolder"
} }
override fun onBind(activity: AppCompatActivity, app: App, item: MessagesSearch, position: Int, adapter: MessagesAdapter) { override fun onBind(
b.searchEdit.removeTextChangedListener(adapter.textWatcher) activity: AppCompatActivity,
b.searchEdit.addTextChangedListener(adapter.textWatcher) app: App,
item: MessagesSearch,
position: Int,
adapter: MessagesAdapter
) {
val watcher = SearchTextWatcher(b, adapter.filter, item)
b.searchEdit.removeTextChangedListener(watcher)
/*b.searchEdit.setOnKeyboardListener(object : TextInputKeyboardEdit.KeyboardListener { if (adapter.items.isEmpty() || adapter.items.size == adapter.allItems.size)
override fun onStateChanged(keyboardEditText: TextInputKeyboardEdit, showing: Boolean) { b.searchLayout.helperText = " "
item.isFocused = showing else
} b.searchLayout.helperText =
})*/ b.root.context.getString(R.string.messages_search_results, adapter.items.size - 1)
/*if (b.searchEdit.text.toString() != item.searchText) {
b.searchEdit.setText(item.searchText) b.searchEdit.setText(item.searchText)
b.searchEdit.setSelection(item.searchText.length)
}*/
//b.searchLayout.helperText = app.getString(R.string.messages_search_results, item.count) b.searchEdit.addTextChangedListener(watcher)
/*if (item.isFocused && !b.searchEdit.isFocused)
b.searchEdit.requestFocus()*/
} }
} }